1. We don't need GetTopLevelParent() here, we already have wxGetTopLevelParent()
[wxWidgets.git] / src / gtk / collpane.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/collpane.cpp
3 // Purpose: wxCollapsiblePane
4 // Author: Francesco Montorsi
5 // Modified By:
6 // Created: 8/10/2006
7 // Id: $Id$
8 // Copyright: (c) Francesco Montorsi
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12
13 // ----------------------------------------------------------------------------
14 // headers
15 // ----------------------------------------------------------------------------
16
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
19
20 #ifdef __WXGTK24__
21
22 #include "wx/collpane.h"
23 #include "wx/gtk/private.h"
24
25 #include <gtk/gtkexpander.h>
26
27 const wxChar wxCollapsiblePaneNameStr[] = wxT("CollapsiblePane");
28
29 // ============================================================================
30 // implementation
31 // ============================================================================
32
33 //-----------------------------------------------------------------------------
34 // "notify::expanded" signal
35 //-----------------------------------------------------------------------------
36
37 extern "C" {
38
39 static void gtk_collapsiblepane_expanded_callback (GObject *object,
40 GParamSpec *param_spec,
41 wxCollapsiblePane *p)
42 {
43 // NB: unlike for the "activate" signal, when this callback is called, if
44 // we try to query the "collapsed" status through p->IsCollapsed(), we
45 // get the right value. I.e. here p->IsCollapsed() will return false if
46 // this callback has been called at the end of a collapsed->expanded
47 // transition and viceversa. Inside the "activate" signal callback
48 // p->IsCollapsed() would return the wrong value!
49
50 wxSize sz;
51 if ( p->IsExpanded() )
52 {
53 // unfortunately there's no clean way to retrieve the minimal size of
54 // the expanded pane in this handler or in other handlers for the
55 // signals generated by user clicks on the GtkExpander button:
56 // p->GetBestSize() or p->GetMinSize() would still return the size for
57 // the collapsed expander even if the collapsed->expanded transition
58 // has already been completed (this because GTK+ queues some resize
59 // calls which still must be processed). So, the only solution to
60 // correctly set the size hints for this window is to calculate the
61 // expanded size ourselves, without relying on p->Get[Best|Min]Size:
62 sz = p->GetMinSize();
63 sz.SetWidth(wxMax(sz.x, p->GetPane()->GetMinSize().x));
64 sz.SetHeight(sz.y + p->GetPane()->GetMinSize().y + 10);
65 }
66 else // collapsed
67 {
68 // same problem described above: using p->Get[Best|Min]Size() here we
69 // would get the size of the control when it is expanded even if the
70 // expanded->collapsed transition should be complete now...
71 // So, we use the size cached at control-creation time...
72 sz = p->m_szCollapsed;
73 }
74
75 // minimal size has priority over the best size so set here our min size
76 p->SetMinSize(sz);
77 p->SetSize(sz);
78
79 wxWindow *top = wxGetTopLevelParent(p);
80 if (top)
81 {
82 // we've changed our size, thus our top level parent needs to relayout
83 // itself
84 top->Layout();
85
86 if (p->IsExpanded())
87 {
88 // force our parent to "fit", i.e. expand so that it can honour
89 // our minimal size
90 top->Fit();
91 }
92 else // correctly
93 {
94 if (top->GetSizer())
95 top->GetSizer()->SetSizeHints(top);
96
97 // use SetClientSize() and not SetSize() otherwise the size for
98 // e.g. a wxFrame with a menubar wouldn't be correctly set
99 top->SetClientSize(sz);
100 }
101 }
102
103 if ( p->m_bIgnoreNextChange )
104 {
105 // change generated programmatically - do not send an event!
106 p->m_bIgnoreNextChange = false;
107 return;
108 }
109
110 // fire an event
111 wxCollapsiblePaneEvent ev(p, p->GetId(), p->IsCollapsed());
112 p->GetEventHandler()->ProcessEvent(ev);
113 }
114 }
115
116 static void
117 gtk_collapsiblepane_insert_callback(wxWindowGTK* parent, wxWindowGTK* child)
118 {
119 // this callback should be used only once to insert the "pane" into the
120 // GtkExpander widget. wxGenericCollapsiblePane::DoAddChild() will check if
121 // it has been called only once (and in any case we would get a warning
122 // from the following call as GtkExpander is a GtkBin and can contain only
123 // a single child!).
124 gtk_container_add (GTK_CONTAINER (parent->m_widget), child->m_widget);
125 }
126
127 //-----------------------------------------------------------------------------
128 // wxCollapsiblePane
129 //-----------------------------------------------------------------------------
130
131 IMPLEMENT_DYNAMIC_CLASS(wxCollapsiblePane, wxGenericCollapsiblePane)
132
133 BEGIN_EVENT_TABLE(wxCollapsiblePane, wxGenericCollapsiblePane)
134 EVT_SIZE(wxCollapsiblePane::OnSize)
135 END_EVENT_TABLE()
136
137 bool wxCollapsiblePane::Create(wxWindow *parent,
138 wxWindowID id,
139 const wxString& label,
140 const wxPoint& pos,
141 const wxSize& size,
142 long style,
143 const wxValidator& val,
144 const wxString& name)
145 {
146 if (gtk_check_version(2,4,0))
147 return wxGenericCollapsiblePane::Create(parent, id, label,
148 pos, size, style, val, name);
149
150 m_needParent = true;
151 m_acceptsFocus = true;
152 m_bIgnoreNextChange = false;
153
154 if ( !PreCreation( parent, pos, size ) ||
155 !wxControl::CreateBase(parent, id, pos, size, style, val, name) )
156 {
157 wxFAIL_MSG( wxT("wxCollapsiblePane creation failed") );
158 return false;
159 }
160
161 m_widget =
162 gtk_expander_new_with_mnemonic(wxGTK_CONV(GTKConvertMnemonics(label)));
163
164 // see the gtk_collapsiblepane_expanded_callback comments to understand why
165 // we connect to the "notify::expanded" signal instead of the more common
166 // "activate" one
167 g_signal_connect(m_widget, "notify::expanded",
168 G_CALLBACK(gtk_collapsiblepane_expanded_callback), this);
169
170 // before creating m_pPane, we need to makesure our own insert callback
171 // will be used
172 m_insertCallback = gtk_collapsiblepane_insert_callback;
173
174 // this the real "pane"
175 m_pPane = new wxWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
176 wxNO_BORDER);
177
178 gtk_widget_show( GTK_WIDGET(m_widget) );
179 m_parent->DoAddChild( this );
180
181 PostCreation(size);
182
183 // remember the size of this control when it's collapsed
184 m_szCollapsed = GetBestSize();
185
186 return true;
187 }
188
189 wxSize wxCollapsiblePane::DoGetBestSize() const
190 {
191 if (!gtk_check_version(2,4,0))
192 {
193 wxASSERT_MSG( m_widget, wxT("DoGetBestSize called before creation") );
194
195 GtkRequisition req;
196 req.width = 2;
197 req.height = 2;
198 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(m_widget) )->size_request )
199 (m_widget, &req );
200
201 // notice that we do not cache our best size here as it changes
202 return wxSize(req.width, req.height);
203 }
204
205 return wxGenericCollapsiblePane::DoGetBestSize();
206 }
207
208 void wxCollapsiblePane::Collapse(bool collapse)
209 {
210 if (!gtk_check_version(2,4,0))
211 {
212 // optimization
213 if (IsCollapsed() == collapse)
214 return;
215
216 // do not send event in next signal handler call
217 m_bIgnoreNextChange = true;
218 gtk_expander_set_expanded(GTK_EXPANDER(m_widget), !collapse);
219 }
220 else
221 wxGenericCollapsiblePane::Collapse(collapse);
222 }
223
224 bool wxCollapsiblePane::IsCollapsed() const
225 {
226 if (!gtk_check_version(2,4,0))
227 return !gtk_expander_get_expanded(GTK_EXPANDER(m_widget));
228
229 return wxGenericCollapsiblePane::IsCollapsed();
230 }
231
232 void wxCollapsiblePane::SetLabel(const wxString &str)
233 {
234 if (!gtk_check_version(2,4,0))
235 gtk_expander_set_label(GTK_EXPANDER(m_widget), str.c_str());
236 else
237 wxGenericCollapsiblePane::SetLabel(str);
238 }
239
240 void wxCollapsiblePane::OnSize(wxSizeEvent &ev)
241 {
242 #if 0 // for debug only
243 wxClientDC dc(this);
244 dc.SetPen(*wxBLACK_PEN);
245 dc.SetBrush(*wxTRANSPARENT_BRUSH);
246 dc.DrawRectangle(wxPoint(0,0), GetSize());
247 dc.SetPen(*wxRED_PEN);
248 dc.DrawRectangle(wxPoint(0,0), GetBestSize());
249 #endif
250
251 // here we need to resize the pane window otherwise, even if the GtkExpander container
252 // is expanded or shrinked, the pane window won't be updated!
253 m_pPane->SetSize(ev.GetSize());
254
255 // we need to explicitely call m_pPane->Layout() or else it won't correctly relayout
256 // (even if SetAutoLayout(true) has been called on it!)
257 m_pPane->Layout();
258 }
259
260 #endif // __WXGTK24__
261