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" 
  20 #if wxUSE_COLLPANE && !defined(__WXUNIVERSAL__) 
  22 #include "wx/collpane.h" 
  23 #include "wx/toplevel.h" 
  27 #include "wx/gtk/private.h" 
  29 // the lines below duplicate the same definitions in collpaneg.cpp, if we have 
  30 // another implementation of this class we should extract them to a common file 
  32 const char wxCollapsiblePaneNameStr
[] = "collapsiblePane"; 
  34 wxDEFINE_EVENT( wxEVT_COMMAND_COLLPANE_CHANGED
, wxCollapsiblePaneEvent 
); 
  36 IMPLEMENT_DYNAMIC_CLASS(wxCollapsiblePaneEvent
, wxCommandEvent
) 
  38 // ============================================================================ 
  40 // ============================================================================ 
  42 //----------------------------------------------------------------------------- 
  43 // "notify::expanded" signal 
  44 //----------------------------------------------------------------------------- 
  49 gtk_collapsiblepane_expanded_callback(GObject 
* WXUNUSED(object
), 
  50                                       GParamSpec 
* WXUNUSED(param_spec
), 
  53     // NB: unlike for the "activate" signal, when this callback is called, if 
  54     //     we try to query the "collapsed" status through p->IsCollapsed(), we 
  55     //     get the right value. I.e. here p->IsCollapsed() will return false if 
  56     //     this callback has been called at the end of a collapsed->expanded 
  57     //     transition and viceversa. Inside the "activate" signal callback 
  58     //     p->IsCollapsed() would return the wrong value! 
  61     if ( p
->IsExpanded() ) 
  63         // NB: we cannot use the p->GetBestSize() or p->GetMinSize() functions 
  64         //     here as they would return the size for the collapsed expander 
  65         //     even if the collapsed->expanded transition has already been 
  66         //     completed; we solve this problem doing: 
  68         sz 
= p
->m_szCollapsed
; 
  70         wxSize panesz 
= p
->GetPane()->GetBestSize(); 
  71         sz
.x 
= wxMax(sz
.x
, panesz
.x
); 
  72         sz
.y 
+= gtk_expander_get_spacing(GTK_EXPANDER(p
->m_widget
)) + panesz
.y
; 
  76         // same problem described above: using p->Get[Best|Min]Size() here we 
  77         // would get the size of the control when it is expanded even if the 
  78         // expanded->collapsed transition should be complete now... 
  79         // So, we use the size cached at control-creation time... 
  80         sz 
= p
->m_szCollapsed
; 
  85     //          p->OnStateChange(sz); 
  86     // here would work work BUT: 
  87     //     1) in the expanded->collapsed transition it provokes a lot of flickering 
  88     //     2) in the collapsed->expanded transition using the "Change status" wxButton 
  89     //        in samples/collpane application some strange warnings would be generated 
  90     //        by the "clearlooks" theme, if that's your theme. 
  92     // So we prefer to use some GTK+ native optimized calls, which prevent too many resize 
  93     // calculations to happen. Note that the following code has been very carefully designed 
  94     // and tested - be VERY careful when changing it! 
  96     // 1) need to update our size hints 
  97     // NB: this function call won't actually do any long operation 
  98     //     (redraw/relayouting/resizing) so that it's flicker-free 
 101     if (p
->HasFlag(wxCP_NO_TLW_RESIZE
)) 
 104         wxCollapsiblePaneEvent 
ev(p
, p
->GetId(), p
->IsCollapsed()); 
 105         p
->HandleWindowEvent(ev
); 
 107         // the user asked to explicitely handle the resizing itself... 
 112         top 
= wxDynamicCast(wxGetTopLevelParent(p
), wxTopLevelWindow
); 
 113     if ( top 
&& top
->GetSizer() ) 
 115         // 2) recalculate minimal size of the top window 
 116         sz 
= top
->GetSizer()->CalcMin(); 
 118         if (top
->m_mainWidget
) 
 120             // 3) MAGIC HACK: if you ever used GtkExpander in a GTK+ program 
 121             //    you know that this magic call is required to make it possible 
 122             //    to shrink the top level window in the expanded->collapsed 
 123             //    transition.  This may be sometimes undesired but *is* 
 124             //    necessary and if you look carefully, all GTK+ programs using 
 125             //    GtkExpander perform this trick (e.g. the standard "open file" 
 126             //    dialog of GTK+>=2.4 is not resizeable when the expander is 
 128             gtk_window_set_resizable (GTK_WINDOW (top
->m_widget
), p
->IsExpanded()); 
 131             top
->SetMinClientSize(sz
); 
 134             top
->SetClientSize(sz
); 
 138     if ( p
->m_bIgnoreNextChange 
) 
 140         // change generated programmatically - do not send an event! 
 141         p
->m_bIgnoreNextChange 
= false; 
 146     wxCollapsiblePaneEvent 
ev(p
, p
->GetId(), p
->IsCollapsed()); 
 147     p
->HandleWindowEvent(ev
); 
 151 void wxCollapsiblePane::AddChildGTK(wxWindowGTK
* child
) 
 153     // should be used only once to insert the "pane" into the 
 154     // GtkExpander widget. wxGenericCollapsiblePane::DoAddChild() will check if 
 155     // it has been called only once (and in any case we would get a warning 
 156     // from the following call as GtkExpander is a GtkBin and can contain only 
 158     gtk_container_add(GTK_CONTAINER(m_widget
), child
->m_widget
); 
 161 //----------------------------------------------------------------------------- 
 163 //----------------------------------------------------------------------------- 
 165 IMPLEMENT_DYNAMIC_CLASS(wxCollapsiblePane
, wxControl
) 
 167 BEGIN_EVENT_TABLE(wxCollapsiblePane
, wxCollapsiblePaneBase
) 
 168     EVT_SIZE(wxCollapsiblePane::OnSize
) 
 171 bool wxCollapsiblePane::Create(wxWindow 
*parent
, 
 173                                const wxString
& label
, 
 177                                const wxValidator
& val
, 
 178                                const wxString
& name
) 
 180     m_bIgnoreNextChange 
= false; 
 182     if ( !PreCreation( parent
, pos
, size 
) || 
 183           !wxControl::CreateBase(parent
, id
, pos
, size
, style
, val
, name
) ) 
 185         wxFAIL_MSG( wxT("wxCollapsiblePane creation failed") ); 
 190         gtk_expander_new_with_mnemonic(wxGTK_CONV(GTKConvertMnemonics(label
))); 
 191     g_object_ref(m_widget
); 
 193     // see the gtk_collapsiblepane_expanded_callback comments to understand why 
 194     // we connect to the "notify::expanded" signal instead of the more common 
 196     g_signal_connect(m_widget
, "notify::expanded", 
 197                      G_CALLBACK(gtk_collapsiblepane_expanded_callback
), this); 
 199     // this the real "pane" 
 200     m_pPane 
= new wxPanel(this, wxID_ANY
, wxDefaultPosition
, wxDefaultSize
, 
 201                           wxTAB_TRAVERSAL
|wxNO_BORDER
, wxS("wxCollapsiblePanePane")); 
 203     gtk_widget_show(m_widget
); 
 204     m_parent
->DoAddChild( this ); 
 208     // we should blend into our parent background 
 209     const wxColour bg 
= parent
->GetBackgroundColour(); 
 210     SetBackgroundColour(bg
); 
 211     m_pPane
->SetBackgroundColour(bg
); 
 213     // remember the size of this control when it's collapsed 
 214     m_szCollapsed 
= GetBestSize(); 
 219 wxSize 
wxCollapsiblePane::DoGetBestSize() const 
 221     wxASSERT_MSG( m_widget
, wxT("DoGetBestSize called before creation") ); 
 226     (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(m_widget
) )->size_request 
) 
 229     // notice that we do not cache our best size here as it changes 
 230     // all times the user expands/hide our pane 
 231     return wxSize(req
.width
, req
.height
); 
 234 void wxCollapsiblePane::Collapse(bool collapse
) 
 237     if (IsCollapsed() == collapse
) 
 240     // do not send event in next signal handler call 
 241     m_bIgnoreNextChange 
= true; 
 242     gtk_expander_set_expanded(GTK_EXPANDER(m_widget
), !collapse
); 
 245 bool wxCollapsiblePane::IsCollapsed() const 
 247     return !gtk_expander_get_expanded(GTK_EXPANDER(m_widget
)); 
 250 void wxCollapsiblePane::SetLabel(const wxString 
&str
) 
 252     gtk_expander_set_label(GTK_EXPANDER(m_widget
), 
 253                            wxGTK_CONV(GTKConvertMnemonics(str
))); 
 255     // FIXME: we need to update our collapsed width in some way but using GetBestSize() 
 256     // we may get the size of the control with the pane size summed up if we are expanded! 
 257     //m_szCollapsed.x = GetBestSize().x; 
 260 void wxCollapsiblePane::OnSize(wxSizeEvent 
&ev
) 
 262 #if 0       // for debug only 
 264     dc
.SetPen(*wxBLACK_PEN
); 
 265     dc
.SetBrush(*wxTRANSPARENT_BRUSH
); 
 266     dc
.DrawRectangle(wxPoint(0,0), GetSize()); 
 267     dc
.SetPen(*wxRED_PEN
); 
 268     dc
.DrawRectangle(wxPoint(0,0), GetBestSize()); 
 271     // here we need to resize the pane window otherwise, even if the GtkExpander container 
 272     // is expanded or shrunk, the pane window won't be updated! 
 273     m_pPane
->SetSize(ev
.GetSize().x
, ev
.GetSize().y 
- m_szCollapsed
.y
); 
 275     // we need to explicitely call m_pPane->Layout() or else it won't correctly relayout 
 276     // (even if SetAutoLayout(true) has been called on it!) 
 281 GdkWindow 
*wxCollapsiblePane::GTKGetWindow(wxArrayGdkWindows
& windows
) const 
 283     GtkWidget 
*label 
= gtk_expander_get_label_widget( GTK_EXPANDER(m_widget
) ); 
 284     windows
.Add( label
->window 
); 
 285     windows
.Add( m_widget
->window 
); 
 290 #endif // wxUSE_COLLPANE && !defined(__WXUNIVERSAL__)