support for GTK3
[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 #if wxUSE_COLLPANE && !defined(__WXUNIVERSAL__)
21
22 #include "wx/collpane.h"
23 #include "wx/toplevel.h"
24 #include "wx/sizer.h"
25 #include "wx/panel.h"
26
27 #include <gtk/gtk.h>
28 #include "wx/gtk/private.h"
29 #include "wx/gtk/private/gtk2-compat.h"
30
31 // the lines below duplicate the same definitions in collpaneg.cpp, if we have
32 // another implementation of this class we should extract them to a common file
33
34 const char wxCollapsiblePaneNameStr[] = "collapsiblePane";
35
36 wxDEFINE_EVENT( wxEVT_COMMAND_COLLPANE_CHANGED, wxCollapsiblePaneEvent );
37
38 IMPLEMENT_DYNAMIC_CLASS(wxCollapsiblePaneEvent, wxCommandEvent)
39
40 // ============================================================================
41 // implementation
42 // ============================================================================
43
44 //-----------------------------------------------------------------------------
45 // "notify::expanded" signal
46 //-----------------------------------------------------------------------------
47
48 extern "C" {
49
50 static void
51 gtk_collapsiblepane_expanded_callback(GObject * WXUNUSED(object),
52 GParamSpec * WXUNUSED(param_spec),
53 wxCollapsiblePane *p)
54 {
55 // NB: unlike for the "activate" signal, when this callback is called, if
56 // we try to query the "collapsed" status through p->IsCollapsed(), we
57 // get the right value. I.e. here p->IsCollapsed() will return false if
58 // this callback has been called at the end of a collapsed->expanded
59 // transition and viceversa. Inside the "activate" signal callback
60 // p->IsCollapsed() would return the wrong value!
61
62 wxSize sz;
63 if ( p->IsExpanded() )
64 {
65 // NB: we cannot use the p->GetBestSize() or p->GetMinSize() functions
66 // here as they would return the size for the collapsed expander
67 // even if the collapsed->expanded transition has already been
68 // completed; we solve this problem doing:
69
70 sz = p->m_szCollapsed;
71
72 wxSize panesz = p->GetPane()->GetBestSize();
73 sz.x = wxMax(sz.x, panesz.x);
74 sz.y += gtk_expander_get_spacing(GTK_EXPANDER(p->m_widget)) + panesz.y;
75 }
76 else // collapsed
77 {
78 // same problem described above: using p->Get[Best|Min]Size() here we
79 // would get the size of the control when it is expanded even if the
80 // expanded->collapsed transition should be complete now...
81 // So, we use the size cached at control-creation time...
82 sz = p->m_szCollapsed;
83 }
84
85 // VERY IMPORTANT:
86 // just calling
87 // p->OnStateChange(sz);
88 // here would work work BUT:
89 // 1) in the expanded->collapsed transition it provokes a lot of flickering
90 // 2) in the collapsed->expanded transition using the "Change status" wxButton
91 // in samples/collpane application some strange warnings would be generated
92 // by the "clearlooks" theme, if that's your theme.
93 //
94 // So we prefer to use some GTK+ native optimized calls, which prevent too many resize
95 // calculations to happen. Note that the following code has been very carefully designed
96 // and tested - be VERY careful when changing it!
97
98 // 1) need to update our size hints
99 // NB: this function call won't actually do any long operation
100 // (redraw/relayout/resize) so that it's flicker-free
101 p->SetMinSize(sz);
102
103 if (p->HasFlag(wxCP_NO_TLW_RESIZE))
104 {
105 // fire an event
106 wxCollapsiblePaneEvent ev(p, p->GetId(), p->IsCollapsed());
107 p->HandleWindowEvent(ev);
108
109 // the user asked to explicitly handle the resizing itself...
110 return;
111 }
112
113 wxTopLevelWindow *
114 top = wxDynamicCast(wxGetTopLevelParent(p), wxTopLevelWindow);
115 if ( top && top->GetSizer() )
116 {
117 // 2) recalculate minimal size of the top window
118 sz = top->GetSizer()->CalcMin();
119
120 if (top->m_mainWidget)
121 {
122 // 3) MAGIC HACK: if you ever used GtkExpander in a GTK+ program
123 // you know that this magic call is required to make it possible
124 // to shrink the top level window in the expanded->collapsed
125 // transition. This may be sometimes undesired but *is*
126 // necessary and if you look carefully, all GTK+ programs using
127 // GtkExpander perform this trick (e.g. the standard "open file"
128 // dialog of GTK+>=2.4 is not resizable when the expander is
129 // collapsed!)
130 gtk_window_set_resizable (GTK_WINDOW (top->m_widget), p->IsExpanded());
131
132 // 4) set size hints
133 top->SetMinClientSize(sz);
134
135 // 5) set size
136 top->SetClientSize(sz);
137 }
138 }
139
140 if ( p->m_bIgnoreNextChange )
141 {
142 // change generated programmatically - do not send an event!
143 p->m_bIgnoreNextChange = false;
144 return;
145 }
146
147 // fire an event
148 wxCollapsiblePaneEvent ev(p, p->GetId(), p->IsCollapsed());
149 p->HandleWindowEvent(ev);
150 }
151 }
152
153 void wxCollapsiblePane::AddChildGTK(wxWindowGTK* child)
154 {
155 // should be used only once to insert the "pane" into the
156 // GtkExpander widget. wxGenericCollapsiblePane::DoAddChild() will check if
157 // it has been called only once (and in any case we would get a warning
158 // from the following call as GtkExpander is a GtkBin and can contain only
159 // a single child!).
160 gtk_container_add(GTK_CONTAINER(m_widget), child->m_widget);
161 }
162
163 //-----------------------------------------------------------------------------
164 // wxCollapsiblePane
165 //-----------------------------------------------------------------------------
166
167 IMPLEMENT_DYNAMIC_CLASS(wxCollapsiblePane, wxControl)
168
169 BEGIN_EVENT_TABLE(wxCollapsiblePane, wxCollapsiblePaneBase)
170 EVT_SIZE(wxCollapsiblePane::OnSize)
171 END_EVENT_TABLE()
172
173 bool wxCollapsiblePane::Create(wxWindow *parent,
174 wxWindowID id,
175 const wxString& label,
176 const wxPoint& pos,
177 const wxSize& size,
178 long style,
179 const wxValidator& val,
180 const wxString& name)
181 {
182 m_bIgnoreNextChange = false;
183
184 if ( !PreCreation( parent, pos, size ) ||
185 !wxControl::CreateBase(parent, id, pos, size, style, val, name) )
186 {
187 wxFAIL_MSG( wxT("wxCollapsiblePane creation failed") );
188 return false;
189 }
190
191 m_widget =
192 gtk_expander_new_with_mnemonic(wxGTK_CONV(GTKConvertMnemonics(label)));
193 g_object_ref(m_widget);
194
195 // see the gtk_collapsiblepane_expanded_callback comments to understand why
196 // we connect to the "notify::expanded" signal instead of the more common
197 // "activate" one
198 g_signal_connect(m_widget, "notify::expanded",
199 G_CALLBACK(gtk_collapsiblepane_expanded_callback), this);
200
201 // this the real "pane"
202 m_pPane = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
203 wxTAB_TRAVERSAL|wxNO_BORDER, wxS("wxCollapsiblePanePane"));
204
205 m_parent->DoAddChild( this );
206
207 PostCreation(size);
208
209 // we should blend into our parent background
210 const wxColour bg = parent->GetBackgroundColour();
211 SetBackgroundColour(bg);
212 m_pPane->SetBackgroundColour(bg);
213
214 // remember the size of this control when it's collapsed
215 m_szCollapsed = GetBestSize();
216
217 return true;
218 }
219
220 wxSize wxCollapsiblePane::DoGetBestSize() const
221 {
222 wxASSERT_MSG( m_widget, wxT("DoGetBestSize called before creation") );
223
224 GtkRequisition req;
225 #ifdef __WXGTK3__
226 gtk_widget_get_preferred_width(m_widget, NULL, &req.width);
227 gtk_widget_get_preferred_height_for_width(m_widget, req.width, NULL, &req.height);
228 #else
229 GTK_WIDGET_GET_CLASS(m_widget)->size_request(m_widget, &req);
230 #endif
231
232 // notice that we do not cache our best size here as it changes
233 // all times the user expands/hide our pane
234 return wxSize(req.width, req.height);
235 }
236
237 void wxCollapsiblePane::Collapse(bool collapse)
238 {
239 // optimization
240 if (IsCollapsed() == collapse)
241 return;
242
243 // do not send event in next signal handler call
244 m_bIgnoreNextChange = true;
245 gtk_expander_set_expanded(GTK_EXPANDER(m_widget), !collapse);
246 }
247
248 bool wxCollapsiblePane::IsCollapsed() const
249 {
250 return !gtk_expander_get_expanded(GTK_EXPANDER(m_widget));
251 }
252
253 void wxCollapsiblePane::SetLabel(const wxString &str)
254 {
255 gtk_expander_set_label(GTK_EXPANDER(m_widget),
256 wxGTK_CONV(GTKConvertMnemonics(str)));
257
258 // FIXME: we need to update our collapsed width in some way but using GetBestSize()
259 // we may get the size of the control with the pane size summed up if we are expanded!
260 //m_szCollapsed.x = GetBestSize().x;
261 }
262
263 void wxCollapsiblePane::OnSize(wxSizeEvent &ev)
264 {
265 #if 0 // for debug only
266 wxClientDC dc(this);
267 dc.SetPen(*wxBLACK_PEN);
268 dc.SetBrush(*wxTRANSPARENT_BRUSH);
269 dc.DrawRectangle(wxPoint(0,0), GetSize());
270 dc.SetPen(*wxRED_PEN);
271 dc.DrawRectangle(wxPoint(0,0), GetBestSize());
272 #endif
273
274 // here we need to resize the pane window otherwise, even if the GtkExpander container
275 // is expanded or shrunk, the pane window won't be updated!
276 m_pPane->SetSize(ev.GetSize().x, ev.GetSize().y - m_szCollapsed.y);
277
278 // we need to explicitly call m_pPane->Layout() or else it won't correctly relayout
279 // (even if SetAutoLayout(true) has been called on it!)
280 m_pPane->Layout();
281 }
282
283
284 GdkWindow *wxCollapsiblePane::GTKGetWindow(wxArrayGdkWindows& windows) const
285 {
286 GtkWidget *label = gtk_expander_get_label_widget( GTK_EXPANDER(m_widget) );
287 windows.Add(gtk_widget_get_window(label));
288 windows.Add(gtk_widget_get_window(m_widget));
289
290 return NULL;
291 }
292
293 #endif // wxUSE_COLLPANE && !defined(__WXUNIVERSAL__)
294