]> git.saurik.com Git - wxWidgets.git/blob - src/generic/statusbr.cpp
Fix horizontal mouse wheel scrolling in wxGTK.
[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 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #if wxUSE_STATUSBAR
19
20 #include "wx/statusbr.h"
21
22 #ifndef WX_PRECOMP
23 #include "wx/settings.h"
24 #include "wx/dcclient.h"
25 #include "wx/toplevel.h"
26 #include "wx/control.h"
27 #endif
28
29 #ifdef __WXGTK20__
30 #include <gtk/gtk.h>
31 #include "wx/gtk/private.h"
32 #include "wx/gtk/private/gtk2-compat.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* WXUNUSED(widget),
61 gint x,
62 gint y,
63 gboolean WXUNUSED(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 const wxString& str = statbar->GetStatusText(n);
76 if (str.empty())
77 return FALSE;
78
79 gtk_tooltip_set_text(tooltip, wxGTK_CONV_SYS(str));
80 return TRUE;
81 }
82 }
83 #endif
84 #endif
85
86 // ----------------------------------------------------------------------------
87 // wxStatusBarGeneric
88 // ----------------------------------------------------------------------------
89
90 BEGIN_EVENT_TABLE(wxStatusBarGeneric, wxWindow)
91 EVT_PAINT(wxStatusBarGeneric::OnPaint)
92 EVT_SIZE(wxStatusBarGeneric::OnSize)
93 #ifdef __WXGTK20__
94 EVT_LEFT_DOWN(wxStatusBarGeneric::OnLeftDown)
95 EVT_RIGHT_DOWN(wxStatusBarGeneric::OnRightDown)
96 #endif
97 EVT_SYS_COLOUR_CHANGED(wxStatusBarGeneric::OnSysColourChanged)
98 END_EVENT_TABLE()
99
100 void wxStatusBarGeneric::Init()
101 {
102 m_borderX = wxTHICK_LINE_BORDER;
103 m_borderY = wxTHICK_LINE_BORDER;
104 }
105
106 wxStatusBarGeneric::~wxStatusBarGeneric()
107 {
108 }
109
110 bool wxStatusBarGeneric::Create(wxWindow *parent,
111 wxWindowID id,
112 long style,
113 const wxString& name)
114 {
115 style |= wxTAB_TRAVERSAL | wxFULL_REPAINT_ON_RESIZE;
116 if ( !wxWindow::Create(parent, id,
117 wxDefaultPosition, wxDefaultSize,
118 style, name) )
119 return false;
120
121 // The status bar should have a themed background
122 SetThemeEnabled( true );
123
124 InitColours();
125
126 #ifdef __WXPM__
127 SetFont(*wxSMALL_FONT);
128 #endif
129
130 int height = (int)((11*GetCharHeight())/10 + 2*GetBorderY());
131 SetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, height);
132
133 SetFieldsCount(1);
134
135 #if defined( __WXGTK20__ )
136 #if GTK_CHECK_VERSION(2,12,0)
137 if (HasFlag(wxSTB_SHOW_TIPS)
138 #ifndef __WXGTK3__
139 && gtk_check_version(2,12,0) == NULL
140 #endif
141 )
142 {
143 g_object_set(m_widget, "has-tooltip", TRUE, NULL);
144 g_signal_connect(m_widget, "query-tooltip",
145 G_CALLBACK(statusbar_query_tooltip), this);
146 }
147 #endif
148 #endif
149
150 return true;
151 }
152
153 wxSize wxStatusBarGeneric::DoGetBestSize() const
154 {
155 int width, height;
156
157 // best width is the width of the parent
158 if (GetParent())
159 GetParent()->GetClientSize(&width, NULL);
160 else
161 width = 80; // a dummy value
162
163 // best height is as calculated above in Create()
164 height = (int)((11*GetCharHeight())/10 + 2*GetBorderY());
165
166 return wxSize(width, height);
167 }
168
169 void wxStatusBarGeneric::DoUpdateStatusText(int number)
170 {
171 wxRect rect;
172 GetFieldRect(number, rect);
173
174 Refresh(true, &rect);
175
176 // it's common to show some text in the status bar before starting a
177 // relatively lengthy operation, ensure that the text is shown to the
178 // user immediately and not after the lengthy operation end
179 Update();
180 }
181
182 void wxStatusBarGeneric::SetStatusWidths(int n, const int widths_field[])
183 {
184 // only set status widths when n == number of statuswindows
185 wxCHECK_RET( (size_t)n == m_panes.GetCount(), wxT("status bar field count mismatch") );
186
187 wxStatusBarBase::SetStatusWidths(n, widths_field);
188
189 // update cache
190 DoUpdateFieldWidths();
191 }
192
193 void wxStatusBarGeneric::DoUpdateFieldWidths()
194 {
195 m_lastClientSize = GetClientSize();
196
197 // recompute the cache of the field widths if the status bar width has changed
198 m_widthsAbs = CalculateAbsWidths(m_lastClientSize.x);
199 }
200
201 bool wxStatusBarGeneric::ShowsSizeGrip() const
202 {
203 if ( !HasFlag(wxSTB_SIZEGRIP) )
204 return false;
205
206 wxTopLevelWindow * const
207 tlw = wxDynamicCast(wxGetTopLevelParent(GetParent()), wxTopLevelWindow);
208 return tlw && !tlw->IsMaximized() && tlw->HasFlag(wxRESIZE_BORDER);
209 }
210
211 void wxStatusBarGeneric::DrawFieldText(wxDC& dc, const wxRect& rect, int i, int textHeight)
212 {
213 wxString text(GetStatusText(i));
214 if (text.empty())
215 return; // optimization
216
217 int xpos = rect.x + wxFIELD_TEXT_MARGIN,
218 maxWidth = rect.width - 2*wxFIELD_TEXT_MARGIN,
219 ypos = (int) (((rect.height - textHeight) / 2) + rect.y + 0.5);
220
221 if (ShowsSizeGrip())
222 {
223 // don't write text over the size grip:
224 // NOTE: overloading DoGetClientSize() and GetClientAreaOrigin() wouldn't
225 // work because the adjustment needs to be done only when drawing
226 // the field text and not also when drawing the background, the
227 // size grip itself, etc
228 if ((GetLayoutDirection() == wxLayout_RightToLeft && i == 0) ||
229 (GetLayoutDirection() != wxLayout_RightToLeft &&
230 i == (int)m_panes.GetCount()-1))
231 {
232 const wxRect& gripRc = GetSizeGripRect();
233
234 // NOTE: we don't need any special treatment wrt to the layout direction
235 // since DrawText() will automatically adjust the origin of the
236 // text accordingly to the layout in use
237
238 maxWidth -= gripRc.width;
239 }
240 }
241
242 // eventually ellipsize the text so that it fits the field width
243
244 wxEllipsizeMode ellmode = (wxEllipsizeMode)-1;
245 if (HasFlag(wxSTB_ELLIPSIZE_START)) ellmode = wxELLIPSIZE_START;
246 else if (HasFlag(wxSTB_ELLIPSIZE_MIDDLE)) ellmode = wxELLIPSIZE_MIDDLE;
247 else if (HasFlag(wxSTB_ELLIPSIZE_END)) ellmode = wxELLIPSIZE_END;
248
249 if (ellmode == (wxEllipsizeMode)-1)
250 {
251 // if we have the wxSTB_SHOW_TIPS we must set the ellipsized flag even if
252 // we don't ellipsize the text but just truncate it
253 if (HasFlag(wxSTB_SHOW_TIPS))
254 SetEllipsizedFlag(i, dc.GetTextExtent(text).GetWidth() > maxWidth);
255
256 dc.SetClippingRegion(rect);
257 }
258 else
259 {
260 text = wxControl::Ellipsize(text, dc,
261 ellmode,
262 maxWidth,
263 wxELLIPSIZE_FLAGS_EXPAND_TABS);
264 // Ellipsize() will do something only if necessary
265
266 // update the ellipsization status for this pane; this is used later to
267 // decide whether a tooltip should be shown or not for this pane
268 // (if we have wxSTB_SHOW_TIPS)
269 SetEllipsizedFlag(i, text != GetStatusText(i));
270 }
271
272 #if defined( __WXGTK__ ) || defined(__WXMAC__)
273 xpos++;
274 ypos++;
275 #endif
276
277 // draw the text
278 dc.DrawText(text, xpos, ypos);
279
280 if (ellmode == (wxEllipsizeMode)-1)
281 dc.DestroyClippingRegion();
282 }
283
284 void wxStatusBarGeneric::DrawField(wxDC& dc, int i, int textHeight)
285 {
286 wxRect rect;
287 GetFieldRect(i, rect);
288
289 if (rect.GetWidth() <= 0)
290 return; // happens when the status bar is shrunk in a very small area!
291
292 int style = m_panes[i].GetStyle();
293 if (style == wxSB_RAISED || style == wxSB_SUNKEN)
294 {
295 // Draw border
296 // For wxSB_SUNKEN: paint a grey background, plus 3-d border (one black rectangle)
297 // Inside this, left and top sides (dark grey). Bottom and right (white).
298 // Reverse it for wxSB_RAISED
299
300 dc.SetPen((style == wxSB_RAISED) ? m_mediumShadowPen : m_hilightPen);
301
302 #ifndef __WXPM__
303
304 // Right and bottom lines
305 dc.DrawLine(rect.x + rect.width, rect.y,
306 rect.x + rect.width, rect.y + rect.height);
307 dc.DrawLine(rect.x + rect.width, rect.y + rect.height,
308 rect.x, rect.y + rect.height);
309
310 dc.SetPen((style == wxSB_RAISED) ? m_hilightPen : m_mediumShadowPen);
311
312 // Left and top lines
313 dc.DrawLine(rect.x, rect.y + rect.height,
314 rect.x, rect.y);
315 dc.DrawLine(rect.x, rect.y,
316 rect.x + rect.width, rect.y);
317 #else
318
319 dc.DrawLine(rect.x + rect.width, rect.height + 2,
320 rect.x, rect.height + 2);
321 dc.DrawLine(rect.x + rect.width, rect.y,
322 rect.x + rect.width, rect.y + rect.height);
323
324 dc.SetPen((style == wxSB_RAISED) ? m_hilightPen : m_mediumShadowPen);
325 dc.DrawLine(rect.x, rect.y,
326 rect.x + rect.width, rect.y);
327 dc.DrawLine(rect.x, rect.y + rect.height,
328 rect.x, rect.y);
329 #endif
330 }
331
332 DrawFieldText(dc, rect, i, textHeight);
333 }
334
335 bool wxStatusBarGeneric::GetFieldRect(int n, wxRect& rect) const
336 {
337 wxCHECK_MSG( (n >= 0) && ((size_t)n < m_panes.GetCount()), false,
338 wxT("invalid status bar field index") );
339
340 // We can be called from the user-defined EVT_SIZE handler in which case
341 // the widths haven't been updated yet and we need to do it now. This is
342 // not very efficient as we keep testing the size but there is no other way
343 // to make the code needing the up-to-date fields sizes in its EVT_SIZE to
344 // work.
345 if ( GetClientSize().x != m_lastClientSize.x )
346 {
347 const_cast<wxStatusBarGeneric*>(this)->DoUpdateFieldWidths();
348 }
349
350 if (m_widthsAbs.IsEmpty())
351 return false;
352
353 rect.x = 0;
354 for ( int i = 0; i < n; i++ )
355 rect.x += m_widthsAbs[i];
356 rect.x += m_borderX;
357
358 rect.y = m_borderY;
359 rect.width = m_widthsAbs[n] - 2*m_borderX;
360 rect.height = m_lastClientSize.y - 2*m_borderY;
361
362 return true;
363 }
364
365 int wxStatusBarGeneric::GetFieldFromPoint(const wxPoint& pt) const
366 {
367 if (m_widthsAbs.IsEmpty())
368 return wxNOT_FOUND;
369
370 // NOTE: we explicitly don't take in count the borders since they are only
371 // useful when rendering the status text, not for hit-test computations
372
373 if (pt.y <= 0 || pt.y >= m_lastClientSize.y)
374 return wxNOT_FOUND;
375
376 int x = 0;
377 for ( size_t i = 0; i < m_panes.GetCount(); i++ )
378 {
379 if (pt.x > x && pt.x < x+m_widthsAbs[i])
380 return i;
381
382 x += m_widthsAbs[i];
383 }
384
385 return wxNOT_FOUND;
386 }
387
388 void wxStatusBarGeneric::InitColours()
389 {
390 #if defined(__WXPM__)
391 m_mediumShadowPen = wxPen(wxColour(127, 127, 127));
392 m_hilightPen = *wxWHITE_PEN;
393
394 SetBackgroundColour(*wxLIGHT_GREY);
395 SetForegroundColour(*wxBLACK);
396 #else // !__WXPM__
397 m_mediumShadowPen = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW));
398 m_hilightPen = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DHILIGHT));
399 #endif // __WXPM__/!__WXPM__
400 }
401
402 void wxStatusBarGeneric::SetMinHeight(int height)
403 {
404 // check that this min height is not less than minimal height for the
405 // current font (min height is as calculated above in Create() except for border)
406 int minHeight = (int)((11*GetCharHeight())/10);
407
408 if ( height > minHeight )
409 SetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, height + 2*m_borderY);
410 }
411
412 wxRect wxStatusBarGeneric::GetSizeGripRect() const
413 {
414 int width, height;
415 wxWindow::DoGetClientSize(&width, &height);
416
417 if (GetLayoutDirection() == wxLayout_RightToLeft)
418 return wxRect(2, 2, height-2, height-4);
419 else
420 return wxRect(width-height-2, 2, height-2, height-4);
421 }
422
423 // ----------------------------------------------------------------------------
424 // wxStatusBarGeneric - event handlers
425 // ----------------------------------------------------------------------------
426
427 void wxStatusBarGeneric::OnPaint(wxPaintEvent& WXUNUSED(event) )
428 {
429 wxPaintDC dc(this);
430
431 #ifdef __WXGTK20__
432 // Draw grip first
433 if ( ShowsSizeGrip() )
434 {
435 const wxRect& rc = GetSizeGripRect();
436 #ifdef __WXGTK3__
437 GtkWidget* toplevel = gtk_widget_get_toplevel(m_widget);
438 GdkRectangle rect;
439 if (toplevel && (!gtk_window_get_resize_grip_area(GTK_WINDOW(toplevel), &rect) ||
440 rect.width == 0 || rect.height == 0))
441 {
442 GtkStyleContext* sc = gtk_widget_get_style_context(toplevel);
443 gtk_style_context_save(sc);
444 gtk_style_context_add_class(sc, GTK_STYLE_CLASS_GRIP);
445 GtkJunctionSides sides = GTK_JUNCTION_CORNER_BOTTOMRIGHT;
446 if (GetLayoutDirection() == wxLayout_RightToLeft)
447 sides = GTK_JUNCTION_CORNER_BOTTOMLEFT;
448 gtk_style_context_set_junction_sides(sc, sides);
449 gtk_render_handle(sc,
450 static_cast<cairo_t*>(dc.GetImpl()->GetCairoContext()),
451 rc.x, rc.y, rc.width, rc.height);
452 gtk_style_context_restore(sc);
453 }
454 #else
455 GdkWindowEdge edge =
456 GetLayoutDirection() == wxLayout_RightToLeft ? GDK_WINDOW_EDGE_SOUTH_WEST :
457 GDK_WINDOW_EDGE_SOUTH_EAST;
458 gtk_paint_resize_grip(gtk_widget_get_style(m_widget),
459 GTKGetDrawingWindow(),
460 gtk_widget_get_state(m_widget),
461 NULL,
462 m_widget,
463 "statusbar",
464 edge,
465 rc.x, rc.y, rc.width, rc.height );
466 #endif
467 }
468 #endif // __WXGTK20__
469
470 if (GetFont().IsOk())
471 dc.SetFont(GetFont());
472
473 // compute char height only once for all panes:
474 int textHeight = dc.GetCharHeight();
475
476 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
477 for (size_t i = 0; i < m_panes.GetCount(); i ++)
478 DrawField(dc, i, textHeight);
479 }
480
481 void wxStatusBarGeneric::OnSysColourChanged(wxSysColourChangedEvent& event)
482 {
483 InitColours();
484
485 // Propagate the event to the non-top-level children
486 wxWindow::OnSysColourChanged(event);
487 }
488
489 #ifdef __WXGTK20__
490 void wxStatusBarGeneric::OnLeftDown(wxMouseEvent& event)
491 {
492 int width, height;
493 GetClientSize(&width, &height);
494
495 GtkWidget* ancestor = gtk_widget_get_toplevel(m_widget);
496 #ifdef __WXGTK3__
497 GdkRectangle rect;
498 if (ancestor && gtk_window_get_resize_grip_area(GTK_WINDOW(ancestor), &rect) &&
499 rect.width && rect.height)
500 {
501 ancestor = NULL;
502 }
503 #endif
504
505 if (ancestor && ShowsSizeGrip() && event.GetX() > width - height)
506 {
507 GdkWindow *source = GTKGetDrawingWindow();
508
509 int org_x = 0;
510 int org_y = 0;
511 gdk_window_get_origin( source, &org_x, &org_y );
512
513 if (GetLayoutDirection() == wxLayout_RightToLeft)
514 {
515 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
516 GDK_WINDOW_EDGE_SOUTH_WEST,
517 1,
518 org_x - event.GetX() + GetSize().x ,
519 org_y + event.GetY(),
520 0);
521 }
522 else
523 {
524 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
525 GDK_WINDOW_EDGE_SOUTH_EAST,
526 1,
527 org_x + event.GetX(),
528 org_y + event.GetY(),
529 0);
530 }
531 }
532 else
533 {
534 event.Skip( true );
535 }
536 }
537
538 void wxStatusBarGeneric::OnRightDown(wxMouseEvent& event)
539 {
540 int width, height;
541 GetClientSize(&width, &height);
542
543 GtkWidget* ancestor = gtk_widget_get_toplevel(m_widget);
544 #ifdef __WXGTK3__
545 GdkRectangle rect;
546 if (ancestor && gtk_window_get_resize_grip_area(GTK_WINDOW(ancestor), &rect) &&
547 rect.width && rect.height)
548 {
549 ancestor = NULL;
550 }
551 #endif
552
553 if (ancestor && ShowsSizeGrip() && event.GetX() > width - height)
554 {
555 GdkWindow *source = GTKGetDrawingWindow();
556
557 int org_x = 0;
558 int org_y = 0;
559 gdk_window_get_origin( source, &org_x, &org_y );
560
561 gtk_window_begin_move_drag (GTK_WINDOW (ancestor),
562 2,
563 org_x + event.GetX(),
564 org_y + event.GetY(),
565 0);
566 }
567 else
568 {
569 event.Skip( true );
570 }
571 }
572 #endif // __WXGTK20__
573
574 void wxStatusBarGeneric::OnSize(wxSizeEvent& event)
575 {
576 DoUpdateFieldWidths();
577
578 event.Skip();
579 }
580
581 #endif // wxUSE_STATUSBAR