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