1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/msw/statbox.cpp
3 // Purpose:     wxStaticBox
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     04/01/98
7 // Copyright:   (c) Julian Smart
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21 
22 
23 #if wxUSE_STATBOX
24 
25 #include "wx/statbox.h"
26 
27 #ifndef WX_PRECOMP
28     #include "wx/app.h"
29     #include "wx/dcclient.h"
30     #include "wx/dcmemory.h"
31     #include "wx/image.h"
32     #include "wx/sizer.h"
33 #endif
34 
35 #include "wx/notebook.h"
36 #include "wx/sysopt.h"
37 
38 #include "wx/msw/uxtheme.h"
39 
40 #include <windowsx.h> // needed by GET_X_LPARAM and GET_Y_LPARAM macros
41 
42 #include "wx/msw/private.h"
43 #include "wx/msw/missing.h"
44 #include "wx/msw/dc.h"
45 #include "wx/msw/private/winstyle.h"
46 
47 namespace
48 {
49 
50 // Offset of the first pixel of the label from the box left border.
51 //
52 // FIXME: value is hardcoded as this is what it is on my system, no idea if
53 //        it's true everywhere
54 const int LABEL_HORZ_OFFSET = 9;
55 
56 // Extra borders around the label on left/right and bottom sides.
57 const int LABEL_HORZ_BORDER = 2;
58 const int LABEL_VERT_BORDER = 2;
59 
60 // Offset of the box contents from left/right/bottom edge (top one is
61 // different, see GetBordersForSizer()). This one is completely arbitrary.
62 const int CHILDREN_OFFSET = 5;
63 
64 } // anonymous namespace
65 
66 // ----------------------------------------------------------------------------
67 // wxWin macros
68 // ----------------------------------------------------------------------------
69 
70 // ============================================================================
71 // implementation
72 // ============================================================================
73 
74 // ----------------------------------------------------------------------------
75 // wxStaticBox
76 // ----------------------------------------------------------------------------
77 
Create(wxWindow * parent,wxWindowID id,const wxString & label,const wxPoint & pos,const wxSize & size,long style,const wxString & name)78 bool wxStaticBox::Create(wxWindow *parent,
79                          wxWindowID id,
80                          const wxString& label,
81                          const wxPoint& pos,
82                          const wxSize& size,
83                          long style,
84                          const wxString& name)
85 {
86     if ( !CreateControl(parent, id, pos, size, style, wxDefaultValidator, name) )
87         return false;
88 
89     if ( !MSWCreateControl(wxT("BUTTON"), label, pos, size) )
90         return false;
91 
92     if (!wxSystemOptions::IsFalse(wxT("msw.staticbox.optimized-paint")))
93     {
94         Bind(wxEVT_PAINT, &wxStaticBox::OnPaint, this);
95 
96         // Our OnPaint() completely erases our background, so don't do it in
97         // WM_ERASEBKGND too to avoid flicker.
98         SetBackgroundStyle(wxBG_STYLE_PAINT);
99     }
100 
101     return true;
102 }
103 
Create(wxWindow * parent,wxWindowID id,wxWindow * labelWin,const wxPoint & pos,const wxSize & size,long style,const wxString & name)104 bool wxStaticBox::Create(wxWindow* parent,
105                          wxWindowID id,
106                          wxWindow* labelWin,
107                          const wxPoint& pos,
108                          const wxSize& size,
109                          long style,
110                          const wxString& name)
111 {
112     wxCHECK_MSG( labelWin, false, wxS("Label window can't be null") );
113 
114     if ( !Create(parent, id, wxString(), pos, size, style, name) )
115         return false;
116 
117     m_labelWin = labelWin;
118     m_labelWin->Reparent(this);
119 
120     PositionLabelWindow();
121 
122     return true;
123 }
124 
PositionLabelWindow()125 void wxStaticBox::PositionLabelWindow()
126 {
127     m_labelWin->SetSize(m_labelWin->GetBestSize());
128     m_labelWin->Move(FromDIP(LABEL_HORZ_OFFSET), 0);
129 }
130 
GetCompositeWindowParts() const131 wxWindowList wxStaticBox::GetCompositeWindowParts() const
132 {
133     wxWindowList parts;
134     if ( m_labelWin )
135         parts.push_back(m_labelWin);
136     return parts;
137 }
138 
MSWGetStyle(long style,WXDWORD * exstyle) const139 WXDWORD wxStaticBox::MSWGetStyle(long style, WXDWORD *exstyle) const
140 {
141     long styleWin = wxStaticBoxBase::MSWGetStyle(style, exstyle);
142 
143     // no need for it anymore, must be removed for wxRadioBox child
144     // buttons to be able to repaint themselves
145     styleWin &= ~WS_CLIPCHILDREN;
146 
147     if ( exstyle )
148     {
149         // We may have children inside this static box, so use this style for
150         // TAB navigation to work if we ever use IsDialogMessage() to implement
151         // it (currently we don't because it's too buggy and implement TAB
152         // navigation ourselves, but this could change in the future).
153         *exstyle |= WS_EX_CONTROLPARENT;
154 
155         if (wxSystemOptions::IsFalse(wxT("msw.staticbox.optimized-paint")))
156             *exstyle |= WS_EX_TRANSPARENT;
157     }
158 
159     styleWin |= BS_GROUPBOX;
160 
161     return styleWin;
162 }
163 
DoGetBestSize() const164 wxSize wxStaticBox::DoGetBestSize() const
165 {
166     wxSize best;
167 
168     // Calculate the size needed by the label
169     int cx, cy;
170     wxGetCharSize(GetHWND(), &cx, &cy, GetFont());
171 
172     int wBox;
173     GetTextExtent(GetLabelText(wxGetWindowText(m_hWnd)), &wBox, &cy);
174 
175     wBox += 3*cx;
176     int hBox = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
177 
178     // If there is a sizer then the base best size is the sizer's minimum
179     if (GetSizer() != NULL)
180     {
181         wxSize cm(GetSizer()->CalcMin());
182         best = ClientToWindowSize(cm);
183         // adjust for a long label if needed
184         best.x = wxMax(best.x, wBox);
185     }
186     // otherwise the best size falls back to the label size
187     else
188     {
189         best = wxSize(wBox, hBox);
190     }
191     return best;
192 }
193 
GetBordersForSizer(int * borderTop,int * borderOther) const194 void wxStaticBox::GetBordersForSizer(int *borderTop, int *borderOther) const
195 {
196     // Base class version doesn't leave enough space at the top when the label
197     // is empty, so we can't use it here, even though the code is pretty
198     // similar.
199     if ( m_labelWin )
200     {
201         *borderTop = m_labelWin->GetSize().y;
202     }
203     else if ( !GetLabel().empty() )
204     {
205         *borderTop = GetCharHeight();
206     }
207     else // No label window nor text.
208     {
209         // This is completely arbitrary, but using the full char height in
210         // this case too seems bad as it leaves too much space at the top
211         // (although it does have the advantage of aligning the controls
212         // inside static boxes with and without labels vertically).
213         *borderTop = 2*FromDIP(CHILDREN_OFFSET);
214     }
215 
216     *borderTop += FromDIP(LABEL_VERT_BORDER);
217 
218     *borderOther = FromDIP(CHILDREN_OFFSET);
219 }
220 
SetBackgroundColour(const wxColour & colour)221 bool wxStaticBox::SetBackgroundColour(const wxColour& colour)
222 {
223     // Do _not_ call the immediate base class method, we don't need to set the
224     // label window (which is the only sub-window of this composite window)
225     // background explicitly because it will almost always be a wxCheckBox or
226     // wxRadioButton which inherits its background from the box anyhow, so
227     // setting it would be at best useless.
228     return wxStaticBoxBase::SetBackgroundColour(colour);
229 }
230 
SetFont(const wxFont & font)231 bool wxStaticBox::SetFont(const wxFont& font)
232 {
233     if ( !wxCompositeWindowSettersOnly<wxStaticBoxBase>::SetFont(font) )
234         return false;
235 
236     // We need to reposition the label as its size may depend on the font.
237     if ( m_labelWin )
238     {
239         PositionLabelWindow();
240     }
241 
242     return true;
243 }
244 
MSWWindowProc(WXUINT nMsg,WXWPARAM wParam,WXLPARAM lParam)245 WXLRESULT wxStaticBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
246 {
247     if ( nMsg == WM_NCHITTEST )
248     {
249         // This code breaks some other processing such as enter/leave tracking
250         // so it's off by default.
251 
252         static int s_useHTClient = -1;
253         if (s_useHTClient == -1)
254             s_useHTClient = wxSystemOptions::GetOptionInt(wxT("msw.staticbox.htclient"));
255         if (s_useHTClient == 1)
256         {
257             int xPos = GET_X_LPARAM(lParam);
258             int yPos = GET_Y_LPARAM(lParam);
259 
260             ScreenToClient(&xPos, &yPos);
261 
262             // Make sure you can drag by the top of the groupbox, but let
263             // other (enclosed) controls get mouse events also
264             if ( yPos < 10 )
265                 return (long)HTCLIENT;
266         }
267     }
268 
269     if ( nMsg == WM_PRINTCLIENT )
270     {
271         // we have to process WM_PRINTCLIENT ourselves as otherwise child
272         // windows' background (eg buttons in radio box) would never be drawn
273         // unless we have a parent with non default background
274 
275         // so check first if we have one
276         if ( !HandlePrintClient((WXHDC)wParam) )
277         {
278             // no, we don't, erase the background ourselves
279             RECT rc;
280             ::GetClientRect(GetHwnd(), &rc);
281             wxDCTemp dc((WXHDC)wParam);
282             PaintBackground(dc, rc);
283         }
284 
285         return 0;
286     }
287 
288     if ( nMsg == WM_UPDATEUISTATE )
289     {
290         // DefWindowProc() redraws just the static box text when it gets this
291         // message and it does it using the standard (blue in standard theme)
292         // colour and not our own label colour that we use in PaintForeground()
293         // resulting in the label mysteriously changing the colour when e.g.
294         // "Alt" is pressed anywhere in the window, see #12497.
295         //
296         // To avoid this we simply refresh the window forcing our own code
297         // redrawing the label in the correct colour to be called. This is
298         // inefficient but there doesn't seem to be anything else we can do.
299         //
300         // Notice that the problem is XP-specific and doesn't arise under later
301         // systems.
302         if ( m_hasFgCol && wxGetWinVersion() == wxWinVersion_XP )
303             Refresh();
304     }
305 
306     return wxControl::MSWWindowProc(nMsg, wParam, lParam);
307 }
308 
309 // ----------------------------------------------------------------------------
310 // static box drawing
311 // ----------------------------------------------------------------------------
312 
313 /*
314    We draw the static box ourselves because it's the only way to prevent it
315    from flickering horribly on resize (because everything inside the box is
316    erased twice: once when the box itself is repainted and second time when
317    the control inside it is repainted) without using WS_EX_TRANSPARENT style as
318    we used to do and which resulted in other problems.
319  */
320 
321 // MSWGetRegionWithoutSelf helper: removes the given rectangle from region
322 static inline void
SubtractRectFromRgn(HRGN hrgn,int left,int top,int right,int bottom)323 SubtractRectFromRgn(HRGN hrgn, int left, int top, int right, int bottom)
324 {
325     AutoHRGN hrgnRect(::CreateRectRgn(left, top, right, bottom));
326     if ( !hrgnRect )
327     {
328         wxLogLastError(wxT("CreateRectRgn()"));
329         return;
330     }
331 
332     ::CombineRgn(hrgn, hrgn, hrgnRect, RGN_DIFF);
333 }
334 
MSWGetRegionWithoutSelf(WXHRGN hRgn,int w,int h)335 void wxStaticBox::MSWGetRegionWithoutSelf(WXHRGN hRgn, int w, int h)
336 {
337     HRGN hrgn = (HRGN)hRgn;
338 
339     // remove the area occupied by the static box borders from the region
340     int borderTop, border;
341     GetBordersForSizer(&borderTop, &border);
342 
343     // top
344     if ( m_labelWin )
345     {
346         // Don't exclude the entire rectangle at the top, we do need to paint
347         // the background of the gap between the label window and the box
348         // frame.
349         const wxRect labelRect = m_labelWin->GetRect();
350         const int gap = FromDIP(LABEL_HORZ_BORDER);
351 
352         SubtractRectFromRgn(hrgn, 0, 0, labelRect.GetLeft() - gap, borderTop);
353         SubtractRectFromRgn(hrgn, labelRect.GetRight() + gap, 0, w, borderTop);
354     }
355     else
356     {
357         SubtractRectFromRgn(hrgn, 0, 0, w, borderTop);
358     }
359 
360     // bottom
361     SubtractRectFromRgn(hrgn, 0, h - border, w, h);
362 
363     // left
364     SubtractRectFromRgn(hrgn, 0, 0, border, h);
365 
366     // right
367     SubtractRectFromRgn(hrgn, w - border, 0, w, h);
368 }
369 
370 namespace {
AdjustRectForRtl(wxLayoutDirection dir,RECT const & childRect,RECT const & boxRect)371 RECT AdjustRectForRtl(wxLayoutDirection dir, RECT const& childRect, RECT const& boxRect) {
372     RECT ret = childRect;
373     if( dir == wxLayout_RightToLeft ) {
374         // The clipping region too is mirrored in RTL layout.
375         // We need to mirror screen coordinates relative to static box window priot to
376         // intersecting with region.
377         ret.right = boxRect.right - childRect.left - boxRect.left;
378         ret.left = boxRect.right - childRect.right - boxRect.left;
379     }
380 
381     return ret;
382 }
383 }
384 
MSWGetRegionWithoutChildren()385 WXHRGN wxStaticBox::MSWGetRegionWithoutChildren()
386 {
387     RECT boxRc;
388     ::GetWindowRect(GetHwnd(), &boxRc);
389     HRGN hrgn = ::CreateRectRgn(boxRc.left, boxRc.top, boxRc.right + 1, boxRc.bottom + 1);
390     bool foundThis = false;
391 
392     // Iterate over all sibling windows as in the old wxWidgets API the
393     // controls appearing inside the static box were created as its siblings
394     // and not children. This is now deprecated but should still work.
395     //
396     // Also notice that we must iterate over all windows, not just all
397     // wxWindows, as there may be composite windows etc.
398     HWND child;
399     for ( child = ::GetWindow(GetHwndOf(GetParent()), GW_CHILD);
400           child;
401           child = ::GetWindow(child, GW_HWNDNEXT) )
402     {
403         if ( ! ::IsWindowVisible(child) )
404         {
405             // if the window isn't visible then it doesn't need clipped
406             continue;
407         }
408 
409         wxMSWWinStyleUpdater updateStyle(child);
410         wxString str(wxGetWindowClass(child));
411         str.UpperCase();
412         if ( str == wxT("BUTTON") && updateStyle.IsOn(BS_GROUPBOX) )
413         {
414             if ( child == GetHwnd() )
415                 foundThis = true;
416 
417             // Any static boxes below this one in the Z-order can't be clipped
418             // since if we have the case where a static box with a low Z-order
419             // is nested inside another static box with a high Z-order then the
420             // nested static box would be painted over. Doing it this way
421             // unfortunately results in flicker if the Z-order of nested static
422             // boxes is not inside (lowest) to outside (highest) but at least
423             // they are still shown.
424             if ( foundThis )
425                 continue;
426         }
427 
428         RECT rc;
429         ::GetWindowRect(child, &rc);
430         rc = AdjustRectForRtl(GetLayoutDirection(), rc, boxRc );
431         if ( ::RectInRegion(hrgn, &rc) )
432         {
433             // need to remove WS_CLIPSIBLINGS from all sibling windows
434             // that are within this staticbox if set
435             if ( updateStyle.IsOn(WS_CLIPSIBLINGS) )
436             {
437                 updateStyle.TurnOff(WS_CLIPSIBLINGS).Apply();
438 
439                 // MSDN: "If you have changed certain window data using
440                 // SetWindowLong, you must call SetWindowPos to have the
441                 // changes take effect."
442                 ::SetWindowPos(child, NULL, 0, 0, 0, 0,
443                                SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
444                                SWP_FRAMECHANGED);
445             }
446 
447             AutoHRGN hrgnChild(::CreateRectRgnIndirect(&rc));
448             ::CombineRgn(hrgn, hrgn, hrgnChild, RGN_DIFF);
449         }
450     }
451 
452     // Also iterate over all children of the static box, we need to clip them
453     // out as well.
454     for ( child = ::GetWindow(GetHwnd(), GW_CHILD);
455           child;
456           child = ::GetWindow(child, GW_HWNDNEXT) )
457     {
458         if ( !::IsWindowVisible(child) )
459         {
460             // if the window isn't visible then it doesn't need clipped
461             continue;
462         }
463 
464         RECT rc;
465         ::GetWindowRect(child, &rc);
466         rc = AdjustRectForRtl(GetLayoutDirection(), rc, boxRc );
467         AutoHRGN hrgnChild(::CreateRectRgnIndirect(&rc));
468         ::CombineRgn(hrgn, hrgn, hrgnChild, RGN_DIFF);
469     }
470 
471     return (WXHRGN)hrgn;
472 }
473 
474 // helper for OnPaint(): really erase the background, i.e. do it even if we
475 // don't have any non default brush for doing it (DoEraseBackground() doesn't
476 // do anything in such case)
PaintBackground(wxDC & dc,const RECT & rc)477 void wxStaticBox::PaintBackground(wxDC& dc, const RECT& rc)
478 {
479     wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
480     HBRUSH hbr = MSWGetBgBrush(impl->GetHDC());
481 
482     // if there is no special brush for painting this control, just use the
483     // solid background colour
484     wxBrush brush;
485     if ( !hbr )
486     {
487         brush = wxBrush(GetParent()->GetBackgroundColour());
488         hbr = GetHbrushOf(brush);
489     }
490 
491     ::FillRect(GetHdcOf(*impl), &rc, hbr);
492 }
493 
PaintForeground(wxDC & dc,const RECT &)494 void wxStaticBox::PaintForeground(wxDC& dc, const RECT&)
495 {
496     wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
497     MSWDefWindowProc(WM_PAINT, (WPARAM)GetHdcOf(*impl), 0);
498 
499 #if wxUSE_UXTHEME
500     // when using XP themes, neither setting the text colour nor transparent
501     // background mode doesn't change anything: the static box def window proc
502     // still draws the label in its own colours, so we need to redraw the text
503     // ourselves if we have a non default fg colour
504     if ( m_hasFgCol && wxUxThemeIsActive() && !m_labelWin )
505     {
506         // draw over the text in default colour in our colour
507         HDC hdc = GetHdcOf(*impl);
508         ::SetTextColor(hdc, GetForegroundColour().GetPixel());
509 
510         // Get dimensions of the label
511         const wxString label = GetLabel();
512 
513         // choose the correct font
514         AutoHFONT font;
515         SelectInHDC selFont;
516         if ( m_hasFont )
517         {
518             selFont.Init(hdc, GetHfontOf(GetFont()));
519         }
520         else // no font set, use the one set by the theme
521         {
522             wxUxThemeHandle hTheme(this, L"BUTTON");
523             if ( hTheme )
524             {
525                 wxUxThemeFont themeFont;
526                 if ( ::GetThemeFont
527                                              (
528                                                 hTheme,
529                                                 hdc,
530                                                 BP_GROUPBOX,
531                                                 GBS_NORMAL,
532                                                 TMT_FONT,
533                                                 themeFont.GetPtr()
534                                              ) == S_OK )
535                 {
536                     font.Init(themeFont.GetLOGFONT());
537                     if ( font )
538                         selFont.Init(hdc, font);
539                 }
540             }
541         }
542 
543         // Get the font extent
544         int width, height;
545         dc.GetTextExtent(wxStripMenuCodes(label, wxStrip_Mnemonics),
546                          &width, &height);
547 
548         // first we need to correctly paint the background of the label
549         // as Windows ignores the brush offset when doing it
550         // NOTE: Border intentionally does not use DIPs in order to match native look
551         const int x = LABEL_HORZ_OFFSET;
552         RECT dimensions = { x, 0, 0, height };
553         dimensions.left = x;
554         dimensions.right = x + width;
555 
556         // need to adjust the rectangle to cover all the label background
557         dimensions.left -= LABEL_HORZ_BORDER;
558         dimensions.right += LABEL_HORZ_BORDER;
559         dimensions.bottom += LABEL_VERT_BORDER;
560 
561         if ( UseBgCol() )
562         {
563             // our own background colour should be used for the background of
564             // the label: this is consistent with the behaviour under pre-XP
565             // systems (i.e. without visual themes) and generally makes sense
566             wxBrush brush = wxBrush(GetBackgroundColour());
567             ::FillRect(hdc, &dimensions, GetHbrushOf(brush));
568         }
569         else // paint parent background
570         {
571             PaintBackground(dc, dimensions);
572         }
573 
574         UINT drawTextFlags = DT_SINGLELINE | DT_VCENTER;
575 
576         // determine the state of UI queues to draw the text correctly under XP
577         // and later systems
578         static const bool isXPorLater = wxGetWinVersion() >= wxWinVersion_XP;
579         if ( isXPorLater )
580         {
581             if ( ::SendMessage(GetHwnd(), WM_QUERYUISTATE, 0, 0) &
582                     UISF_HIDEACCEL )
583             {
584                 drawTextFlags |= DT_HIDEPREFIX;
585             }
586         }
587 
588         // now draw the text
589         RECT rc2 = { x, 0, x + width, height };
590         ::DrawText(hdc, label.t_str(), label.length(), &rc2,
591                    drawTextFlags);
592     }
593 #endif // wxUSE_UXTHEME
594 }
595 
OnPaint(wxPaintEvent & WXUNUSED (event))596 void wxStaticBox::OnPaint(wxPaintEvent& WXUNUSED(event))
597 {
598     RECT rc;
599     ::GetClientRect(GetHwnd(), &rc);
600     wxPaintDC dc(this);
601 
602     // No need to do anything if the client rectangle is empty and, worse,
603     // doing it would result in an assert when creating the bitmap below.
604     if ( !rc.right || !rc.bottom )
605         return;
606 
607     // draw the entire box in a memory DC
608     wxMemoryDC memdc(&dc);
609     wxBitmap bitmap(rc.right, rc.bottom);
610     memdc.SelectObject(bitmap);
611 
612     PaintBackground(memdc, rc);
613     PaintForeground(memdc, rc);
614 
615     // now only blit the static box border itself, not the interior, to avoid
616     // flicker when background is drawn below
617     //
618     // note that it seems to be faster to do 4 small blits here and then paint
619     // directly into wxPaintDC than painting background in wxMemoryDC and then
620     // blitting everything at once to wxPaintDC, this is why we do it like this
621     int borderTop, border;
622     GetBordersForSizer(&borderTop, &border);
623 
624     // top
625     if ( m_labelWin )
626     {
627         // We also have to exclude the area taken by the label window,
628         // otherwise there would be flicker when it draws itself on top of it.
629         const wxRect labelRect = m_labelWin->GetRect();
630 
631         // We also leave a small border around label window to make it appear
632         // more similarly to a plain text label.
633         const int gap = FromDIP(LABEL_HORZ_BORDER);
634 
635         dc.Blit(border, 0,
636                 labelRect.GetLeft() - gap - border,
637                 borderTop,
638                 &memdc, border, 0);
639         dc.Blit(labelRect.GetRight() + gap, 0,
640                 rc.right - (labelRect.GetRight() + gap),
641                 borderTop,
642                 &memdc, border, 0);
643     }
644     else
645     {
646         dc.Blit(border, 0, rc.right - border, borderTop,
647                 &memdc, border, 0);
648     }
649 
650     // bottom
651     dc.Blit(border, rc.bottom - border, rc.right - border, border,
652             &memdc, border, rc.bottom - border);
653     // left
654     dc.Blit(0, 0, border, rc.bottom,
655             &memdc, 0, 0);
656     // right (note that upper and bottom right corners were already part of the
657     // first two blits so we shouldn't overwrite them here to avoi flicker)
658     dc.Blit(rc.right - border, borderTop,
659             border, rc.bottom - borderTop - border,
660             &memdc, rc.right - border, borderTop);
661 
662 
663     // create the region excluding box children
664     AutoHRGN hrgn((HRGN)MSWGetRegionWithoutChildren());
665     RECT rcWin;
666     ::GetWindowRect(GetHwnd(), &rcWin);
667     ::OffsetRgn(hrgn, -rcWin.left, -rcWin.top);
668 
669     // and also the box itself
670     MSWGetRegionWithoutSelf((WXHRGN) hrgn, rc.right, rc.bottom);
671     wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
672     HDCClipper clipToBg(GetHdcOf(*impl), hrgn);
673 
674     // paint the inside of the box (excluding box itself and child controls)
675     PaintBackground(dc, rc);
676 }
677 
678 #endif // wxUSE_STATBOX
679