1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        wx/compositewin.h
3 // Purpose:     wxCompositeWindow<> declaration
4 // Author:      Vadim Zeitlin
5 // Created:     2011-01-02
6 // Copyright:   (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
7 // Licence:     wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9 
10 #ifndef _WX_COMPOSITEWIN_H_
11 #define _WX_COMPOSITEWIN_H_
12 
13 #include "wx/window.h"
14 #include "wx/containr.h"
15 
16 class WXDLLIMPEXP_FWD_CORE wxToolTip;
17 
18 // NB: This is an experimental and, as for now, undocumented class used only by
19 //     wxWidgets itself internally. Don't use it in your code until its API is
20 //     officially stabilized unless you are ready to change it with the next
21 //     wxWidgets release.
22 
23 // ----------------------------------------------------------------------------
24 // wxCompositeWindow is a helper for implementing composite windows: to define
25 // a class using subwindows, simply inherit from it specialized with the real
26 // base class name and implement GetCompositeWindowParts() pure virtual method.
27 // ----------------------------------------------------------------------------
28 
29 // This is the base class of wxCompositeWindow which takes care of propagating
30 // colours, fonts etc changes to all the children, but doesn't bother with
31 // handling their events or focus. There should be rarely any need to use it
32 // rather than the full wxCompositeWindow.
33 
34 // The template parameter W must be a wxWindow-derived class.
35 template <class W>
36 class wxCompositeWindowSettersOnly : public W
37 {
38 public:
39     typedef W BaseWindowClass;
40 
41     // Override all wxWindow methods which must be forwarded to the composite
42     // window parts.
43 
44     // Attribute setters group.
45     //
46     // NB: Unfortunately we can't factor out the call for the setter itself
47     //     into DoSetForAllParts() because we can't call the function passed to
48     //     it non-virtually and we need to do this to avoid infinite recursion,
49     //     so we work around this by calling the method of this object itself
50     //     manually in each function.
SetForegroundColour(const wxColour & colour)51     virtual bool SetForegroundColour(const wxColour& colour) wxOVERRIDE
52     {
53         if ( !BaseWindowClass::SetForegroundColour(colour) )
54             return false;
55 
56         SetForAllParts(&wxWindowBase::SetForegroundColour, colour);
57 
58         return true;
59     }
60 
SetBackgroundColour(const wxColour & colour)61     virtual bool SetBackgroundColour(const wxColour& colour) wxOVERRIDE
62     {
63         if ( !BaseWindowClass::SetBackgroundColour(colour) )
64             return false;
65 
66         SetForAllParts(&wxWindowBase::SetBackgroundColour, colour);
67 
68         return true;
69     }
70 
SetFont(const wxFont & font)71     virtual bool SetFont(const wxFont& font) wxOVERRIDE
72     {
73         if ( !BaseWindowClass::SetFont(font) )
74             return false;
75 
76         SetForAllParts(&wxWindowBase::SetFont, font);
77 
78         return true;
79     }
80 
SetCursor(const wxCursor & cursor)81     virtual bool SetCursor(const wxCursor& cursor) wxOVERRIDE
82     {
83         if ( !BaseWindowClass::SetCursor(cursor) )
84             return false;
85 
86         SetForAllParts(&wxWindowBase::SetCursor, cursor);
87 
88         return true;
89     }
90 
SetLayoutDirection(wxLayoutDirection dir)91     virtual void SetLayoutDirection(wxLayoutDirection dir) wxOVERRIDE
92     {
93         BaseWindowClass::SetLayoutDirection(dir);
94 
95         SetForAllParts(&wxWindowBase::SetLayoutDirection, dir);
96 
97         // The child layout almost invariably depends on the layout direction,
98         // so redo it when it changes.
99         //
100         // However avoid doing it when we're called from wxWindow::Create() in
101         // wxGTK as the derived window is not fully created yet and calling its
102         // SetSize() may be unexpected. This does mean that any future calls to
103         // SetLayoutDirection(wxLayout_Default) wouldn't result in a re-layout
104         // neither, but then we're not supposed to be called with it at all.
105         if ( dir != wxLayout_Default )
106             this->SetSize(-1, -1, -1, -1, wxSIZE_FORCE);
107     }
108 
109 #if wxUSE_TOOLTIPS
DoSetToolTipText(const wxString & tip)110     virtual void DoSetToolTipText(const wxString &tip) wxOVERRIDE
111     {
112         BaseWindowClass::DoSetToolTipText(tip);
113 
114         // Use a variable to disambiguate between SetToolTip() overloads.
115         void (wxWindowBase::*func)(const wxString&) = &wxWindowBase::SetToolTip;
116 
117         SetForAllParts(func, tip);
118     }
119 
DoSetToolTip(wxToolTip * tip)120     virtual void DoSetToolTip(wxToolTip *tip) wxOVERRIDE
121     {
122         BaseWindowClass::DoSetToolTip(tip);
123 
124         SetForAllParts(&wxWindowBase::CopyToolTip, tip);
125     }
126 #endif // wxUSE_TOOLTIPS
127 
128 protected:
129     // Trivial but necessary default ctor.
wxCompositeWindowSettersOnly()130     wxCompositeWindowSettersOnly()
131     {
132     }
133 
134 private:
135     // Must be implemented by the derived class to return all children to which
136     // the public methods we override should forward to.
137     virtual wxWindowList GetCompositeWindowParts() const = 0;
138 
139     template <class T, class TArg, class R>
SetForAllParts(R (wxWindowBase::* func)(TArg),T arg)140     void SetForAllParts(R (wxWindowBase::*func)(TArg), T arg)
141     {
142         // Simply call the setters for all parts of this composite window.
143         const wxWindowList parts = GetCompositeWindowParts();
144         for ( wxWindowList::const_iterator i = parts.begin();
145               i != parts.end();
146               ++i )
147         {
148             wxWindow * const child = *i;
149 
150             // Allow NULL elements in the list, this makes the code of derived
151             // composite controls which may have optionally shown children
152             // simpler and it doesn't cost us much here.
153             if ( child )
154                 (child->*func)(arg);
155         }
156     }
157 
158     wxDECLARE_NO_COPY_TEMPLATE_CLASS(wxCompositeWindowSettersOnly, W);
159 };
160 
161 // The real wxCompositeWindow itself, inheriting all the setters defined above.
162 template <class W>
163 class wxCompositeWindow : public wxCompositeWindowSettersOnly<W>
164 {
165 public:
SetFocus()166     virtual void SetFocus() wxOVERRIDE
167     {
168         wxSetFocusToChild(this, NULL);
169     }
170 
171 protected:
172     // Default ctor sets things up for handling children events correctly.
wxCompositeWindow()173     wxCompositeWindow()
174     {
175         this->Bind(wxEVT_CREATE, &wxCompositeWindow::OnWindowCreate, this);
176     }
177 
178 private:
OnWindowCreate(wxWindowCreateEvent & event)179     void OnWindowCreate(wxWindowCreateEvent& event)
180     {
181         event.Skip();
182 
183         // Attach a few event handlers to all parts of the composite window.
184         // This makes the composite window behave more like a simple control
185         // and allows other code (such as wxDataViewCtrl's inline editing
186         // support) to hook into its event processing.
187 
188         wxWindow *child = event.GetWindow();
189 
190         // Check that it's one of our children: it could also be this window
191         // itself (for which we don't need to handle focus at all) or one of
192         // its grandchildren and we don't want to bind to those as child
193         // controls are supposed to be well-behaved and get their own focus
194         // event if any of their children get focus anyhow, so binding to them
195         // would only result in duplicate events.
196         //
197         // Notice that we can't use GetCompositeWindowParts() here because the
198         // member variables that are typically used in its implementation in
199         // the derived classes would typically not be initialized yet, as this
200         // event is generated by "m_child = new wxChildControl(this, ...)" code
201         // before "m_child" is assigned.
202         if ( child->GetParent() != this )
203             return;
204 
205         child->Bind(wxEVT_SET_FOCUS, &wxCompositeWindow::OnSetFocus, this);
206 
207         child->Bind(wxEVT_KILL_FOCUS, &wxCompositeWindow::OnKillFocus, this);
208 
209         // Some events should be only handled for non-toplevel children. For
210         // example, we want to close the control in wxDataViewCtrl when Enter
211         // is pressed in the inline editor, but not when it's pressed in a
212         // popup dialog it opens.
213         wxWindow *win = child;
214         while ( win && win != this )
215         {
216             if ( win->IsTopLevel() )
217                 return;
218             win = win->GetParent();
219         }
220 
221         child->Bind(wxEVT_CHAR, &wxCompositeWindow::OnChar, this);
222     }
223 
OnChar(wxKeyEvent & event)224     void OnChar(wxKeyEvent& event)
225     {
226         if ( !this->ProcessWindowEvent(event) )
227             event.Skip();
228     }
229 
OnSetFocus(wxFocusEvent & event)230     void OnSetFocus(wxFocusEvent& event)
231     {
232         event.Skip();
233 
234         // When a child of a composite window gains focus, the entire composite
235         // focus gains focus as well -- unless it had it already.
236         //
237         // We suppose that we hadn't had focus if the event doesn't carry the
238         // previously focused window as it normally means that it comes from
239         // outside of this program.
240         wxWindow* const oldFocus = event.GetWindow();
241         if ( !oldFocus || oldFocus->GetMainWindowOfCompositeControl() != this )
242         {
243             wxFocusEvent eventThis(wxEVT_SET_FOCUS, this->GetId());
244             eventThis.SetEventObject(this);
245             eventThis.SetWindow(event.GetWindow());
246 
247             this->ProcessWindowEvent(eventThis);
248         }
249     }
250 
OnKillFocus(wxFocusEvent & event)251     void OnKillFocus(wxFocusEvent& event)
252     {
253         // Ignore focus changes within the composite control:
254         wxWindow *win = event.GetWindow();
255         while ( win )
256         {
257             if ( win == this )
258             {
259                 event.Skip();
260                 return;
261             }
262 
263             // Note that we don't use IsTopLevel() check here, because we do
264             // want to ignore focus changes going to toplevel window that have
265             // the composite control as its parent; these would typically be
266             // some kind of control's popup window.
267             win = win->GetParent();
268         }
269 
270         // The event shouldn't be ignored, forward it to the main control:
271         if ( !this->ProcessWindowEvent(event) )
272             event.Skip();
273     }
274 
275     wxDECLARE_NO_COPY_TEMPLATE_CLASS(wxCompositeWindow, W);
276 };
277 
278 #endif // _WX_COMPOSITEWIN_H_
279