1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/collpane.cpp
3 // Purpose: wxCollapsiblePane
4 // Author: Francesco Montorsi
8 // Copyright: (c) Francesco Montorsi
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 // ----------------------------------------------------------------------------
15 // ----------------------------------------------------------------------------
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
22 #include "wx/collpane.h"
23 #include <gtk/gtkexpander.h>
26 // ============================================================================
28 // ============================================================================
32 class myTimer
: public wxTimer
37 myTimer(wxCollapsiblePane
*pp
) {p
=pp
;}
41 //wxLogDebug(wxT("%d"), p->IsCollapsed());
43 wxSize sz
= p
->GetBestSize();
44 wxLogDebug(wxT("our best size is now: %d-%d"), sz
.GetWidth(), sz
.GetHeight());
50 const wxChar wxCollapsiblePaneNameStr
[] = wxT("CollapsiblePane");
52 //-----------------------------------------------------------------------------
53 // "notify::expanded" signal
54 //-----------------------------------------------------------------------------
58 static void gtk_collapsiblepane_expanded_callback (GObject
*object
,
59 GParamSpec
*param_spec
,
62 // NB: unlike for the "activate" signal, when this callback is called, if we try to
63 // query the "collapsed" status through p->IsCollapsed(), we get the right value.
64 // I.e. here p->IsCollapsed() will return false if this callback has been called
65 // at the end of a collapsed->expanded transition and viceversa.
66 // Inside the "activate" signal callback p->IsCollapsed() would return the wrong value!
68 wxLogDebug("gtk_collapsiblepane_expanded_callback - IsCollapsed says %d", p
->IsCollapsed());
71 if (!p
->IsCollapsed())
73 // unfortunately there's no clean way to retrieve the minimal size of the expanded pane
74 // in this handler or in other handlers for the signals generated by user clicks on the
75 // GtkExpander button: p->GetBestSize() or p->GetMinSize() would still return the size for
76 // the collapsed expander even if the collapsed->expanded transition has already been
77 // completed (this because GTK+ queues some resize calls which still must be processed).
78 // So, the only solution to correctly set the size hints for this window is to calculate
79 // the expanded size ourselves, without relying on p->Get[Best|Min]Size:
81 sz
.SetWidth( wxMax(sz
.GetWidth(), p
->GetPane()->GetMinSize().GetWidth()) );
82 sz
.SetHeight( sz
.GetHeight() + p
->GetPane()->GetMinSize().GetHeight() + 10 );
86 // same problem described above: using p->Get[Best|Min]Size() here we would get the size
87 // of the control when it is expanded even if the expanded->collapsed transition should be
89 // So, we use the size cached at control-creation time...
90 sz
= p
->m_szCollapsed
;
93 wxLogDebug(wxT("gtk_collapsiblepane_expanded_callback - my min size is now: %d-%d"),
94 sz
.GetWidth(), sz
.GetHeight());
96 // minimal size has priority over the best size so set here our min size
100 wxWindow
*top
= p
->GetTopLevelParent();
103 // we've changed our size, thus our top level parent needs to relayout itself
106 // FIXME: this makes wxGenericCollapsiblePane behave as the user expect but
107 // maybe there are cases where this is unwanted!
110 // FIXME: the SetSizeHints() call would be required also for GTK+ for the
111 // expanded->collapsed transition.
112 // Unfortunately if we enable this line, then the GTK+ top window
113 // won't always be resized by the SetClientSize() call below!
114 // As a side effect of this dirty fix, the minimal size for the
115 // pane window is not set in GTK+ and the user can hide it shrinking
116 // the "top" window...
117 if (p
->IsCollapsed())
119 top
->GetSizer()->SetSizeHints(top
);
121 if (p
->IsCollapsed())
123 // NB: we need to use SetClientSize() and not SetSize() otherwise the size for
124 // windows like e.g. wxFrames with wxMenubars won't be correctly set
125 top
->SetClientSize(sz
);
129 // force our parent to "fit", i.e. expand so that it can honour
135 if (p
->m_bIgnoreNextChange
)
137 // change generated programmatically - do not send an event!
138 p
->m_bIgnoreNextChange
= false;
143 wxCollapsiblePaneEvent
ev(p
, p
->GetId(), p
->IsCollapsed());
144 p
->GetEventHandler()->ProcessEvent(ev
);
148 static void gtk_collapsiblepane_insert_callback( wxWindowGTK
* parent
, wxWindowGTK
* child
)
150 // this callback should be used only once to insert the "pane" into
151 // the GtkExpander widget. wxGenericCollapsiblePane::DoAddChild() will check
152 // if it has been called only once (and in any case we would get a warning
153 // from the following call as GtkExpander is a GtkBin and can contain only a
155 gtk_container_add (GTK_CONTAINER (parent
->m_widget
), child
->m_widget
);
159 //-----------------------------------------------------------------------------
161 //-----------------------------------------------------------------------------
163 IMPLEMENT_DYNAMIC_CLASS(wxCollapsiblePane
, wxGenericCollapsiblePane
)
165 BEGIN_EVENT_TABLE(wxCollapsiblePane
, wxGenericCollapsiblePane
)
166 EVT_SIZE(wxCollapsiblePane::OnSize
)
169 bool wxCollapsiblePane::Create( wxWindow
*parent
, wxWindowID id
,
170 const wxString
& label
,
174 const wxValidator
& val
,
175 const wxString
& name
)
177 if (gtk_check_version(2,4,0)) {wxASSERT(0);
178 return wxGenericCollapsiblePane::Create(parent
, id
, label
, pos
, size
, style
, val
, name
);}
181 m_acceptsFocus
= true;
182 m_bIgnoreNextChange
= false;
184 if (!PreCreation( parent
, pos
, size
) ||
185 !wxControl::CreateBase(parent
, id
, pos
, size
, style
, val
, name
))
187 wxFAIL_MSG( wxT("wxCollapsiblePane creation failed") );
191 m_widget
= gtk_expander_new(label
.c_str());
193 // see the gtk_collapsiblepane_expanded_callback comments to understand why we connect
194 // to the "notify::expanded" signal instead of the more common "activate" one
195 g_signal_connect(m_widget
, "notify::expanded",
196 G_CALLBACK(gtk_collapsiblepane_expanded_callback
), this);
198 // before creating m_pPane, we need to makesure our own insert callback will be used
199 m_insertCallback
= gtk_collapsiblepane_insert_callback
;
201 // this the real "pane"
202 m_pPane
= new wxWindow(this, wxID_ANY
, wxDefaultPosition
, wxDefaultSize
, wxNO_BORDER
);
204 gtk_widget_show( GTK_WIDGET(m_widget
) );
205 m_parent
->DoAddChild( this );
210 // remember the size of this control when it's collapsed
211 m_szCollapsed
= GetBestSize();
214 myTimer *t = new myTimer(this);
221 wxSize
wxCollapsiblePane::DoGetBestSize() const
223 if (!gtk_check_version(2,4,0))
225 //return wxControl::DoGetBestSize(); // need not to cache the best size!
227 wxASSERT_MSG( m_widget
, wxT("DoGetBestSize called before creation") );
232 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(m_widget
) )->size_request
)
235 wxSize
best(req
.width
, req
.height
);
236 //CacheBestSize(best);
240 return wxGenericCollapsiblePane::DoGetBestSize();
243 void wxCollapsiblePane::Collapse(bool collapse
)
245 if (!gtk_check_version(2,4,0))
248 if (IsCollapsed() == collapse
)
251 // do not send event in next signal handler call
252 m_bIgnoreNextChange
= true;
253 gtk_expander_set_expanded(GTK_EXPANDER(m_widget
), !collapse
);
256 wxGenericCollapsiblePane::Collapse(collapse
);
259 bool wxCollapsiblePane::IsCollapsed() const
261 if (!gtk_check_version(2,4,0))
262 return !gtk_expander_get_expanded(GTK_EXPANDER(m_widget
));
264 return wxGenericCollapsiblePane::IsCollapsed();
267 void wxCollapsiblePane::SetLabel(const wxString
&str
)
269 if (!gtk_check_version(2,4,0))
270 gtk_expander_set_label(GTK_EXPANDER(m_widget
), str
.c_str());
272 wxGenericCollapsiblePane::SetLabel(str
);
275 void wxCollapsiblePane::OnSize(wxSizeEvent
&ev
)
277 #if 0 // for debug only
279 dc
.SetPen(*wxBLACK_PEN
);
280 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
281 dc
.DrawRectangle(wxPoint(0,0), GetSize());
282 dc
.SetPen(*wxRED_PEN
);
283 dc
.DrawRectangle(wxPoint(0,0), GetBestSize());
286 //wxLogDebug(wxT("wxCollapsiblePane::OnSize"));
288 // here we need to resize the pane window otherwise, even if the GtkExpander container
289 // is expanded or shrinked, the pane window won't be updated!
290 m_pPane
->SetSize(ev
.GetSize());
292 // we need to explicitely call m_pPane->Layout() or else it won't correctly relayout
293 // (even if SetAutoLayout(true) has been called on it!)
297 #endif // __WXGTK24__