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: statusbr.cpp 57542 2008-12-25 13:03:24Z VZ $
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#endif
27
28#ifdef __WXGTK20__
29    #include <gtk/gtk.h>
30    #include "wx/gtk/win_gtk.h"
31#endif
32
33// we only have to do it here when we use wxStatusBarGeneric in addition to the
34// standard wxStatusBar class, if wxStatusBarGeneric is the same as
35// wxStatusBar, then the corresponding IMPLEMENT_DYNAMIC_CLASS is already in
36// common/statbar.cpp
37#if defined(__WXMAC__) || \
38    (defined(wxUSE_NATIVE_STATUSBAR) && wxUSE_NATIVE_STATUSBAR)
39    #include "wx/generic/statusbr.h"
40
41    IMPLEMENT_DYNAMIC_CLASS(wxStatusBarGeneric, wxWindow)
42#endif // wxUSE_NATIVE_STATUSBAR
43
44BEGIN_EVENT_TABLE(wxStatusBarGeneric, wxWindow)
45    EVT_PAINT(wxStatusBarGeneric::OnPaint)
46    EVT_LEFT_DOWN(wxStatusBarGeneric::OnLeftDown)
47    EVT_RIGHT_DOWN(wxStatusBarGeneric::OnRightDown)
48    EVT_SYS_COLOUR_CHANGED(wxStatusBarGeneric::OnSysColourChanged)
49END_EVENT_TABLE()
50
51// Default status border dimensions
52#define         wxTHICK_LINE_BORDER 2
53
54void wxStatusBarGeneric::Init()
55{
56    m_borderX = wxTHICK_LINE_BORDER;
57    m_borderY = wxTHICK_LINE_BORDER;
58}
59
60wxStatusBarGeneric::~wxStatusBarGeneric()
61{
62}
63
64bool wxStatusBarGeneric::Create(wxWindow *parent,
65                                wxWindowID id,
66                                long style,
67                                const wxString& name)
68{
69    style |= wxTAB_TRAVERSAL | wxFULL_REPAINT_ON_RESIZE;
70    if ( !wxWindow::Create(parent, id,
71                           wxDefaultPosition, wxDefaultSize,
72                           style, name) )
73        return false;
74
75    // The status bar should have a themed background
76    SetThemeEnabled( true );
77
78    InitColours();
79
80#ifdef __WXPM__
81    SetFont(*wxSMALL_FONT);
82#endif
83
84    wxCoord y;
85    {
86        // Set the height according to the font and the border size
87        wxClientDC dc(this);
88        dc.SetFont(GetFont());
89
90        dc.GetTextExtent(_T("X"), NULL, &y );
91    }
92    int height = (int)( (11*y)/10 + 2*GetBorderY());
93
94    SetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, height);
95
96    SetFieldsCount(1);
97
98    return true;
99}
100
101
102wxSize wxStatusBarGeneric::DoGetBestSize() const
103{
104    int width, height;
105
106    // best width is the width of the parent
107    GetParent()->GetClientSize(&width, NULL);
108
109    // best height is as calculated above in Create
110    wxClientDC dc((wxWindow*)this);
111    dc.SetFont(GetFont());
112    wxCoord y;
113    dc.GetTextExtent(_T("X"), NULL, &y );
114    height = (int)( (11*y)/10 + 2*GetBorderY());
115
116    return wxSize(width, height);
117}
118
119void wxStatusBarGeneric::SetFieldsCount(int number, const int *widths)
120{
121    wxASSERT_MSG( number >= 0, _T("negative number of fields in wxStatusBar?") );
122
123    int i;
124    for(i = m_nFields; i < number; ++i)
125        m_statusStrings.Add( wxEmptyString );
126
127    for (i = m_nFields - 1; i >= number; --i)
128        m_statusStrings.RemoveAt(i);
129
130    // forget the old cached pixel widths
131    m_widthsAbs.Empty();
132
133    wxStatusBarBase::SetFieldsCount(number, widths);
134
135    wxASSERT_MSG( m_nFields == (int)m_statusStrings.GetCount(),
136                  _T("This really should never happen, can we do away with m_nFields here?") );
137}
138
139void wxStatusBarGeneric::SetStatusText(const wxString& text, int number)
140{
141    wxCHECK_RET( (number >= 0) && (number < m_nFields),
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
161wxString wxStatusBarGeneric::GetStatusText(int n) const
162{
163    wxCHECK_MSG( (n >= 0) && (n < m_nFields), wxEmptyString,
164                 _T("invalid status bar field index") );
165
166    return m_statusStrings[n];
167}
168
169void wxStatusBarGeneric::SetStatusWidths(int n, const int widths_field[])
170{
171    // only set status widths, when n == number of statuswindows
172    wxCHECK_RET( n == m_nFields, _T("status bar field count mismatch") );
173
174    // delete the old widths in any case - this function may be used to reset
175    // the widths to the default (all equal)
176    // MBN: this is incompatible with at least wxMSW and wxMAC and not
177    //      documented, but let's keep it for now
178    ReinitWidths();
179
180    // forget the old cached pixel widths
181    m_widthsAbs.Empty();
182
183    if ( !widths_field )
184    {
185        // not an error, see the comment above
186        Refresh();
187        return;
188    }
189
190    wxStatusBarBase::SetStatusWidths(n, widths_field);
191}
192
193void wxStatusBarGeneric::OnPaint(wxPaintEvent& WXUNUSED(event) )
194{
195    wxPaintDC dc(this);
196
197    dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
198#ifdef __WXGTK20__
199    // Draw grip first
200    if (HasFlag( wxST_SIZEGRIP ))
201    {
202        int width, height;
203        GetClientSize(&width, &height);
204
205        if (GetLayoutDirection() == wxLayout_RightToLeft)
206        {
207            gtk_paint_resize_grip( m_widget->style,
208                               GTK_PIZZA(m_wxwindow)->bin_window,
209                               (GtkStateType) GTK_WIDGET_STATE (m_widget),
210                               NULL,
211                               m_widget,
212                               "statusbar",
213                               GDK_WINDOW_EDGE_SOUTH_WEST,
214                               2, 2, height-2, height-4 );
215        }
216        else
217        {
218            gtk_paint_resize_grip( m_widget->style,
219                               GTK_PIZZA(m_wxwindow)->bin_window,
220                               (GtkStateType) GTK_WIDGET_STATE (m_widget),
221                               NULL,
222                               m_widget,
223                               "statusbar",
224                               GDK_WINDOW_EDGE_SOUTH_EAST,
225                               width-height-2, 2, height-2, height-4 );
226        }
227    }
228#endif
229
230    if (GetFont().Ok())
231        dc.SetFont(GetFont());
232
233    dc.SetBackgroundMode(wxTRANSPARENT);
234
235#ifdef __WXPM__
236    wxColour vColor;
237
238    vColor = wxSystemSettings::GetColour(wxSYS_COLOUR_MENUBAR);
239    ::WinFillRect(dc.m_hPS, &dc.m_vRclPaint, vColor.GetPixel());
240#endif
241
242    for (int i = 0; i < m_nFields; i ++)
243        DrawField(dc, i);
244}
245
246void wxStatusBarGeneric::DrawFieldText(wxDC& dc, int i)
247{
248    int leftMargin = 2;
249
250    wxRect rect;
251    GetFieldRect(i, rect);
252
253    wxString text(GetStatusText(i));
254
255    long x = 0, y = 0;
256
257    dc.GetTextExtent(text, &x, &y);
258
259    int xpos = rect.x + leftMargin;
260    int ypos = (int) (((rect.height - y) / 2 ) + rect.y + 0.5) ;
261
262#if defined( __WXGTK__ ) || defined(__WXMAC__)
263    xpos++;
264    ypos++;
265#endif
266
267    dc.SetClippingRegion(rect.x, rect.y, rect.width, rect.height);
268
269    dc.DrawText(text, xpos, ypos);
270
271    dc.DestroyClippingRegion();
272}
273
274void wxStatusBarGeneric::DrawField(wxDC& dc, int i)
275{
276    wxRect rect;
277    GetFieldRect(i, rect);
278
279    int style = wxSB_NORMAL;
280    if (m_statusStyles)
281        style = m_statusStyles[i];
282
283    if (style != wxSB_FLAT)
284    {
285        // Draw border
286        // For wxSB_NORMAL:
287        // Have grey background, plus 3-d border -
288        // One black rectangle.
289        // Inside this, left and top sides - dark grey. Bottom and right -
290        // white.
291        // Reverse it for wxSB_RAISED
292
293        dc.SetPen((style == wxSB_RAISED) ? m_mediumShadowPen : m_hilightPen);
294
295    #ifndef __WXPM__
296
297        // Right and bottom lines
298        dc.DrawLine(rect.x + rect.width, rect.y,
299                    rect.x + rect.width, rect.y + rect.height);
300        dc.DrawLine(rect.x + rect.width, rect.y + rect.height,
301                    rect.x, rect.y + rect.height);
302
303        dc.SetPen((style == wxSB_RAISED) ? m_hilightPen : m_mediumShadowPen);
304
305        // Left and top lines
306        dc.DrawLine(rect.x, rect.y + rect.height,
307               rect.x, rect.y);
308        dc.DrawLine(rect.x, rect.y,
309            rect.x + rect.width, rect.y);
310    #else
311
312        dc.DrawLine(rect.x + rect.width, rect.height + 2,
313                    rect.x, rect.height + 2);
314        dc.DrawLine(rect.x + rect.width, rect.y,
315                    rect.x + rect.width, rect.y + rect.height);
316
317        dc.SetPen((style == wxSB_RAISED) ? m_hilightPen : m_mediumShadowPen);
318        dc.DrawLine(rect.x, rect.y,
319                    rect.x + rect.width, rect.y);
320        dc.DrawLine(rect.x, rect.y + rect.height,
321                   rect.x, rect.y);
322
323#endif
324    }
325
326    DrawFieldText(dc, i);
327}
328
329  // Get the position and size of the field's internal bounding rectangle
330bool wxStatusBarGeneric::GetFieldRect(int n, wxRect& rect) const
331{
332    wxCHECK_MSG( (n >= 0) && (n < m_nFields), false,
333                 _T("invalid status bar field index") );
334
335    // FIXME: workarounds for OS/2 bugs have nothing to do here (VZ)
336    int width, height;
337#ifdef __WXPM__
338    GetSize(&width, &height);
339#else
340    GetClientSize(&width, &height);
341#endif
342
343    // we cache m_widthsAbs between calls and recompute it if client
344    // width has changed (or when it is initially empty)
345    if ( m_widthsAbs.IsEmpty() || (m_lastClientWidth != width) )
346    {
347        wxConstCast(this, wxStatusBarGeneric)->
348            m_widthsAbs = CalculateAbsWidths(width);
349        // remember last width for which we have recomputed the widths in pixels
350        wxConstCast(this, wxStatusBarGeneric)->
351            m_lastClientWidth = width;
352    }
353
354    rect.x = 0;
355    for ( int i = 0; i < n; i++ )
356    {
357        rect.x += m_widthsAbs[i];
358    }
359
360    rect.x += m_borderX;
361    rect.y = m_borderY;
362
363    rect.width = m_widthsAbs[n] - 2*m_borderX;
364    rect.height = height - 2*m_borderY;
365
366    return true;
367}
368
369// Initialize colours
370void wxStatusBarGeneric::InitColours()
371{
372#if defined(__WXPM__)
373    m_mediumShadowPen = wxPen(wxColour(127, 127, 127), 1, wxSOLID);
374    m_hilightPen = *wxWHITE_PEN;
375
376    SetBackgroundColour(*wxLIGHT_GREY);
377    SetForegroundColour(*wxBLACK);
378#else // !__WXPM__
379    m_mediumShadowPen = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW));
380    m_hilightPen = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DHILIGHT));
381#endif // __WXPM__/!__WXPM__
382}
383
384// Responds to colour changes, and passes event on to children.
385void wxStatusBarGeneric::OnSysColourChanged(wxSysColourChangedEvent& event)
386{
387    InitColours();
388
389    // Propagate the event to the non-top-level children
390    wxWindow::OnSysColourChanged(event);
391}
392
393void wxStatusBarGeneric::SetMinHeight(int height)
394{
395    // check that this min height is not less than minimal height for the
396    // current font
397    wxClientDC dc(this);
398    wxCoord y;
399    dc.GetTextExtent( wxT("X"), NULL, &y );
400
401    if ( height > (11*y)/10 )
402    {
403        SetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, height + 2*m_borderY);
404    }
405}
406
407void wxStatusBarGeneric::OnLeftDown(wxMouseEvent& event)
408{
409#ifdef __WXGTK20__
410    int width, height;
411    GetClientSize(&width, &height);
412
413    if (HasFlag( wxST_SIZEGRIP ) && (event.GetX() > width-height))
414    {
415        GtkWidget *ancestor = gtk_widget_get_toplevel( m_widget );
416
417        if (!GTK_IS_WINDOW (ancestor))
418            return;
419
420        GdkWindow *source = GTK_PIZZA(m_wxwindow)->bin_window;
421
422        int org_x = 0;
423        int org_y = 0;
424        gdk_window_get_origin( source, &org_x, &org_y );
425
426        if (GetLayoutDirection() == wxLayout_RightToLeft)
427        {
428            gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
429                                  GDK_WINDOW_EDGE_SOUTH_WEST,
430                                  1,
431                                  org_x - event.GetX() + GetSize().x ,
432                                  org_y + event.GetY(),
433                                  0);
434        }
435        else
436        {
437            gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
438                                  GDK_WINDOW_EDGE_SOUTH_EAST,
439                                  1,
440                                  org_x + event.GetX(),
441                                  org_y + event.GetY(),
442                                  0);
443        }
444    }
445    else
446    {
447        event.Skip( true );
448    }
449#else
450    event.Skip( true );
451#endif
452}
453
454void wxStatusBarGeneric::OnRightDown(wxMouseEvent& event)
455{
456#ifdef __WXGTK20__
457    int width, height;
458    GetClientSize(&width, &height);
459
460    if (HasFlag( wxST_SIZEGRIP ) && (event.GetX() > width-height))
461    {
462        GtkWidget *ancestor = gtk_widget_get_toplevel( m_widget );
463
464        if (!GTK_IS_WINDOW (ancestor))
465            return;
466
467        GdkWindow *source = GTK_PIZZA(m_wxwindow)->bin_window;
468
469        int org_x = 0;
470        int org_y = 0;
471        gdk_window_get_origin( source, &org_x, &org_y );
472
473        gtk_window_begin_move_drag (GTK_WINDOW (ancestor),
474                                2,
475                                org_x + event.GetX(),
476                                org_y + event.GetY(),
477                                0);
478    }
479    else
480    {
481        event.Skip( true );
482    }
483#else
484    event.Skip( true );
485#endif
486}
487
488#endif // wxUSE_STATUSBAR
489