1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/collpane.cpp
3 // Purpose:     wxCollapsiblePane
4 // Author:      Francesco Montorsi
5 // Modified By:
6 // Created:     8/10/2006
7 // Copyright:   (c) Francesco Montorsi
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 
12 // ----------------------------------------------------------------------------
13 // headers
14 // ----------------------------------------------------------------------------
15 
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18 
19 #if wxUSE_COLLPANE && !defined(__WXUNIVERSAL__)
20 
21 #include "wx/collpane.h"
22 #include "wx/toplevel.h"
23 #include "wx/sizer.h"
24 #include "wx/panel.h"
25 
26 #include "wx/gtk/private.h"
27 
28 // the lines below duplicate the same definitions in collpaneg.cpp, if we have
29 // another implementation of this class we should extract them to a common file
30 
31 const char wxCollapsiblePaneNameStr[] = "collapsiblePane";
32 
33 wxDEFINE_EVENT( wxEVT_COLLAPSIBLEPANE_CHANGED, wxCollapsiblePaneEvent );
34 
35 wxIMPLEMENT_DYNAMIC_CLASS(wxCollapsiblePaneEvent, wxCommandEvent);
36 
37 // ============================================================================
38 // implementation
39 // ============================================================================
40 
41 //-----------------------------------------------------------------------------
42 // "notify::expanded" signal
43 //-----------------------------------------------------------------------------
44 
45 extern "C" {
46 
47 static void
gtk_collapsiblepane_expanded_callback(GObject * WXUNUSED (object),GParamSpec * WXUNUSED (param_spec),wxCollapsiblePane * p)48 gtk_collapsiblepane_expanded_callback(GObject * WXUNUSED(object),
49                                       GParamSpec * WXUNUSED(param_spec),
50                                       wxCollapsiblePane *p)
51 {
52     // When the pane is expanded or collapsed, its best size changes, so it
53     // needs to be invalidated in any case.
54     p->InvalidateBestSize();
55 
56     if (!p->HasFlag(wxCP_NO_TLW_RESIZE))
57     {
58         wxTopLevelWindow *
59             top = wxDynamicCast(wxGetTopLevelParent(p), wxTopLevelWindow);
60 
61         // If we want to automatically resize the entire TLW to adopt to the
62         // new pane size, we also need to invalidate the cached best sizes of
63         // all the intermediate windows to ensure that it's recalculated
64         // correctly when doing the layout below.
65         for ( wxWindow* w = p->GetParent(); w != top; w = w->GetParent() )
66         {
67             w->InvalidateBestSize();
68         }
69 
70         if ( top && top->GetSizer() )
71         {
72             // 2) recalculate minimal size of the top window
73             const wxSize sz = top->GetSizer()->CalcMin();
74 
75             if (top->m_mainWidget)
76             {
77                 // 3) MAGIC HACK: if you ever used GtkExpander in a GTK+ program
78                 //    you know that this magic call is required to make it possible
79                 //    to shrink the top level window in the expanded->collapsed
80                 //    transition.  This may be sometimes undesired but *is*
81                 //    necessary and if you look carefully, all GTK+ programs using
82                 //    GtkExpander perform this trick (e.g. the standard "open file"
83                 //    dialog of GTK+>=2.4 is not resizable when the expander is
84                 //    collapsed!)
85                 gtk_window_set_resizable (GTK_WINDOW (top->m_widget), p->IsExpanded());
86 
87                 // 4) set size hints
88                 top->SetMinClientSize(sz);
89 
90                 // 5) set size
91                 top->SetClientSize(sz);
92             }
93         }
94     }
95 
96     if ( p->m_bIgnoreNextChange )
97     {
98         // change generated programmatically - do not send an event!
99         p->m_bIgnoreNextChange = false;
100         return;
101     }
102 
103     // fire an event
104     wxCollapsiblePaneEvent ev(p, p->GetId(), p->IsCollapsed());
105     p->HandleWindowEvent(ev);
106 }
107 }
108 
AddChildGTK(wxWindowGTK * child)109 void wxCollapsiblePane::AddChildGTK(wxWindowGTK* child)
110 {
111     // should be used only once to insert the "pane" into the
112     // GtkExpander widget. wxGenericCollapsiblePane::DoAddChild() will check if
113     // it has been called only once (and in any case we would get a warning
114     // from the following call as GtkExpander is a GtkBin and can contain only
115     // a single child!).
116     gtk_container_add(GTK_CONTAINER(m_widget), child->m_widget);
117 }
118 
119 //-----------------------------------------------------------------------------
120 // wxCollapsiblePane
121 //-----------------------------------------------------------------------------
122 
123 wxIMPLEMENT_DYNAMIC_CLASS(wxCollapsiblePane, wxControl);
124 
wxBEGIN_EVENT_TABLE(wxCollapsiblePane,wxCollapsiblePaneBase)125 wxBEGIN_EVENT_TABLE(wxCollapsiblePane, wxCollapsiblePaneBase)
126     EVT_SIZE(wxCollapsiblePane::OnSize)
127 wxEND_EVENT_TABLE()
128 
129 bool wxCollapsiblePane::Create(wxWindow *parent,
130                                wxWindowID id,
131                                const wxString& label,
132                                const wxPoint& pos,
133                                const wxSize& size,
134                                long style,
135                                const wxValidator& val,
136                                const wxString& name)
137 {
138     m_bIgnoreNextChange = false;
139 
140     if ( !PreCreation( parent, pos, size ) ||
141           !wxControl::CreateBase(parent, id, pos, size, style, val, name) )
142     {
143         wxFAIL_MSG( wxT("wxCollapsiblePane creation failed") );
144         return false;
145     }
146 
147     m_widget =
148         gtk_expander_new_with_mnemonic(wxGTK_CONV(GTKConvertMnemonics(label)));
149     g_object_ref(m_widget);
150 
151     // Connect to the "notify::expanded" signal instead of the more common
152     // "activate" one in order to use the new state in our callback, which is
153     // more convenient e.g. because calling GetBestSize() returns the suitable
154     // size for the new state.
155     g_signal_connect(m_widget, "notify::expanded",
156                      G_CALLBACK(gtk_collapsiblepane_expanded_callback), this);
157 
158     // this the real "pane"
159     m_pPane = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
160                           wxTAB_TRAVERSAL|wxNO_BORDER, wxS("wxCollapsiblePanePane"));
161 
162     m_parent->DoAddChild( this );
163 
164     PostCreation(size);
165 
166     // we should blend into our parent background
167     const wxColour bg = parent->GetBackgroundColour();
168     SetBackgroundColour(bg);
169     m_pPane->SetBackgroundColour(bg);
170 
171     // remember the size of this control when it's collapsed
172     m_szCollapsed = GTKGetPreferredSize(m_widget);
173 
174     return true;
175 }
176 
DoGetBestSize() const177 wxSize wxCollapsiblePane::DoGetBestSize() const
178 {
179     wxASSERT_MSG( m_widget, wxT("DoGetBestSize called before creation") );
180 
181     wxSize sz = m_szCollapsed;
182 
183     if ( IsExpanded() )
184     {
185         const wxSize panesz = m_pPane->GetBestSize();
186         sz.x = wxMax(sz.x, panesz.x);
187         sz.y += gtk_expander_get_spacing(GTK_EXPANDER(m_widget)) + panesz.y;
188     }
189 
190     return sz;
191 }
192 
Collapse(bool collapse)193 void wxCollapsiblePane::Collapse(bool collapse)
194 {
195     // optimization
196     if (IsCollapsed() == collapse)
197         return;
198 
199     // do not send event in next signal handler call
200     m_bIgnoreNextChange = true;
201     gtk_expander_set_expanded(GTK_EXPANDER(m_widget), !collapse);
202 }
203 
IsCollapsed() const204 bool wxCollapsiblePane::IsCollapsed() const
205 {
206     return !gtk_expander_get_expanded(GTK_EXPANDER(m_widget));
207 }
208 
SetLabel(const wxString & str)209 void wxCollapsiblePane::SetLabel(const wxString &str)
210 {
211     gtk_expander_set_label(GTK_EXPANDER(m_widget),
212                            wxGTK_CONV(GTKConvertMnemonics(str)));
213 
214     // FIXME: we need to update our collapsed width in some way but using GetBestSize()
215     // we may get the size of the control with the pane size summed up if we are expanded!
216     //m_szCollapsed.x = GetBestSize().x;
217 }
218 
OnSize(wxSizeEvent & ev)219 void wxCollapsiblePane::OnSize(wxSizeEvent &ev)
220 {
221 #if 0       // for debug only
222     wxClientDC dc(this);
223     dc.SetPen(*wxBLACK_PEN);
224     dc.SetBrush(*wxTRANSPARENT_BRUSH);
225     dc.DrawRectangle(wxPoint(0,0), GetSize());
226     dc.SetPen(*wxRED_PEN);
227     dc.DrawRectangle(wxPoint(0,0), GetBestSize());
228 #endif
229 
230     // here we need to resize the pane window otherwise, even if the GtkExpander container
231     // is expanded or shrunk, the pane window won't be updated!
232     int h = ev.GetSize().y - m_szCollapsed.y;
233     if (h < 0) h = 0;
234     m_pPane->SetSize(ev.GetSize().x, h);
235 
236     // we need to explicitly call m_pPane->Layout() or else it won't correctly relayout
237     // (even if SetAutoLayout(true) has been called on it!)
238     m_pPane->Layout();
239 }
240 
241 
GTKGetWindow(wxArrayGdkWindows & windows) const242 GdkWindow *wxCollapsiblePane::GTKGetWindow(wxArrayGdkWindows& windows) const
243 {
244     GtkWidget *label = gtk_expander_get_label_widget( GTK_EXPANDER(m_widget) );
245     windows.Add(gtk_widget_get_window(label));
246     windows.Add(gtk_widget_get_window(m_widget));
247 
248     return NULL;
249 }
250 
251 #endif // wxUSE_COLLPANE && !defined(__WXUNIVERSAL__)
252 
253