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