1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/infobar.cpp
3 // Purpose:     wxInfoBar implementation for GTK
4 // Author:      Vadim Zeitlin
5 // Created:     2009-09-27
6 // Copyright:   (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
7 // Licence:     wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9 
10 // ============================================================================
11 // declarations
12 // ============================================================================
13 
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17 
18 // for compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20 
21 #ifdef __BORLANDC__
22     #pragma hdrstop
23 #endif
24 
25 #include "wx/infobar.h"
26 
27 #if wxUSE_INFOBAR && defined(wxHAS_NATIVE_INFOBAR)
28 
29 #ifndef WX_PRECOMP
30 #endif // WX_PRECOMP
31 
32 #include "wx/vector.h"
33 #include "wx/stockitem.h"
34 
35 #include "wx/gtk/private.h"
36 #include "wx/gtk/private/messagetype.h"
37 
38 // ----------------------------------------------------------------------------
39 // local classes
40 // ----------------------------------------------------------------------------
41 
42 class wxInfoBarGTKImpl
43 {
44 public:
wxInfoBarGTKImpl()45     wxInfoBarGTKImpl()
46     {
47         m_label = NULL;
48         m_close = NULL;
49     }
50 
51     // label for the text shown in the bar
52     GtkWidget *m_label;
53 
54     // the default close button, NULL if not needed (m_buttons is not empty) or
55     // not created yet
56     GtkWidget *m_close;
57 
58     // information about the buttons added using AddButton()
59     struct Button
60     {
ButtonwxInfoBarGTKImpl::Button61         Button(GtkWidget *button_, int id_)
62             : button(button_),
63               id(id_)
64         {
65         }
66 
67         GtkWidget *button;
68         int id;
69     };
70     typedef wxVector<Button> Buttons;
71 
72     Buttons m_buttons;
73 };
74 
75 // ----------------------------------------------------------------------------
76 // local functions
77 // ----------------------------------------------------------------------------
78 
79 namespace
80 {
81 
UseNative()82 inline bool UseNative()
83 {
84 #ifdef __WXGTK3__
85     return true;
86 #else
87     // native GtkInfoBar widget is only available in GTK+ 2.18 and later
88     return gtk_check_version(2, 18, 0) == 0;
89 #endif
90 }
91 
92 } // anonymous namespace
93 
94 extern "C"
95 {
96 
wxgtk_infobar_response(GtkInfoBar * WXUNUSED (infobar),gint btnid,wxInfoBar * win)97 static void wxgtk_infobar_response(GtkInfoBar * WXUNUSED(infobar),
98                                    gint btnid,
99                                    wxInfoBar *win)
100 {
101     win->GTKResponse(btnid);
102 }
103 
wxgtk_infobar_close(GtkInfoBar * WXUNUSED (infobar),wxInfoBar * win)104 static void wxgtk_infobar_close(GtkInfoBar * WXUNUSED(infobar),
105                                 wxInfoBar *win)
106 {
107     win->GTKResponse(wxID_CANCEL);
108 }
109 
110 } // extern "C" section with GTK+ callbacks
111 
112 // ============================================================================
113 // wxInfoBar implementation
114 // ============================================================================
115 
Create(wxWindow * parent,wxWindowID winid)116 bool wxInfoBar::Create(wxWindow *parent, wxWindowID winid)
117 {
118     if ( !UseNative() )
119         return wxInfoBarGeneric::Create(parent, winid);
120 
121     m_impl = new wxInfoBarGTKImpl;
122 
123     // this control is created initially hidden
124     Hide();
125     if ( !CreateBase(parent, winid) )
126         return false;
127 
128     // create the info bar widget itself
129     m_widget = gtk_info_bar_new();
130     wxCHECK_MSG( m_widget, false, "failed to create GtkInfoBar" );
131     g_object_ref(m_widget);
132 
133     // also create a label which will be used to show our message
134     m_impl->m_label = gtk_label_new("");
135     gtk_widget_show(m_impl->m_label);
136 
137     GtkWidget * const
138         contentArea = gtk_info_bar_get_content_area(GTK_INFO_BAR(m_widget));
139     wxCHECK_MSG( contentArea, false, "failed to get GtkInfoBar content area" );
140     gtk_container_add(GTK_CONTAINER(contentArea), m_impl->m_label);
141 
142     // finish creation and connect to all the signals we're interested in
143     m_parent->DoAddChild(this);
144 
145     PostCreation(wxDefaultSize);
146 
147     GTKConnectWidget("response", G_CALLBACK(wxgtk_infobar_response));
148     GTKConnectWidget("close", G_CALLBACK(wxgtk_infobar_close));
149 
150     // Work around GTK+ bug https://bugzilla.gnome.org/show_bug.cgi?id=710888
151     // by disabling the transition when showing it: without this, it's not
152     // shown at all.
153     //
154     // Compile-time check is needed because GtkRevealer is new in 3.10.
155 #if GTK_CHECK_VERSION(3, 10, 0)
156     // Run-time check is needed because the bug was introduced in 3.10 and
157     // fixed in 3.22.29 (see 6b4d95e86dabfcdaa805fbf068a99e19736a39a4 and a
158     // couple of previous commits in GTK+ repository).
159     if ( gtk_check_version(3, 10, 0) == NULL &&
160             gtk_check_version(3, 22, 29) != NULL )
161     {
162         GObject* const
163             revealer = gtk_widget_get_template_child(GTK_WIDGET(m_widget),
164                                                      GTK_TYPE_INFO_BAR,
165                                                      "revealer");
166         if ( revealer )
167         {
168             gtk_revealer_set_transition_type(GTK_REVEALER (revealer),
169                                              GTK_REVEALER_TRANSITION_TYPE_NONE);
170             gtk_revealer_set_transition_duration(GTK_REVEALER (revealer), 0);
171         }
172     }
173 #endif // GTK+ >= 3.10
174 
175     return true;
176 }
177 
~wxInfoBar()178 wxInfoBar::~wxInfoBar()
179 {
180     delete m_impl;
181 }
182 
ShowMessage(const wxString & msg,int flags)183 void wxInfoBar::ShowMessage(const wxString& msg, int flags)
184 {
185     if ( !UseNative() )
186     {
187         wxInfoBarGeneric::ShowMessage(msg, flags);
188         return;
189     }
190 
191     // if we don't have any buttons, create a standard close one to give the
192     // user at least some way to close the bar
193     if ( m_impl->m_buttons.empty() && !m_impl->m_close )
194     {
195         m_impl->m_close = GTKAddButton(wxID_CLOSE);
196     }
197 
198     GtkMessageType type;
199     if ( wxGTKImpl::ConvertMessageTypeFromWX(flags, &type) )
200         gtk_info_bar_set_message_type(GTK_INFO_BAR(m_widget), type);
201     gtk_label_set_text(GTK_LABEL(m_impl->m_label), wxGTK_CONV(msg));
202 
203     if ( !IsShown() )
204         Show();
205 
206     UpdateParent();
207 }
208 
Dismiss()209 void wxInfoBar::Dismiss()
210 {
211     if ( !UseNative() )
212     {
213         wxInfoBarGeneric::Dismiss();
214         return;
215     }
216 
217     Hide();
218 
219     UpdateParent();
220 }
221 
GTKResponse(int btnid)222 void wxInfoBar::GTKResponse(int btnid)
223 {
224     wxCommandEvent event(wxEVT_BUTTON, btnid);
225     event.SetEventObject(this);
226 
227     if ( !HandleWindowEvent(event) )
228         Dismiss();
229 }
230 
GTKAddButton(wxWindowID btnid,const wxString & label)231 GtkWidget *wxInfoBar::GTKAddButton(wxWindowID btnid, const wxString& label)
232 {
233     // as GTK+ lays out the buttons vertically, adding another button changes
234     // our best size (at least in vertical direction)
235     InvalidateBestSize();
236 
237     GtkWidget *button = gtk_info_bar_add_button
238                         (
239                             GTK_INFO_BAR(m_widget),
240                             (label.empty()
241                                 ? GTKConvertMnemonics(wxGetStockGtkID(btnid))
242                                 : label).utf8_str(),
243                             btnid
244                         );
245 
246     wxASSERT_MSG( button, "unexpectedly failed to add button to info bar" );
247 
248     return button;
249 }
250 
AddButton(wxWindowID btnid,const wxString & label)251 void wxInfoBar::AddButton(wxWindowID btnid, const wxString& label)
252 {
253     if ( !UseNative() )
254     {
255         wxInfoBarGeneric::AddButton(btnid, label);
256         return;
257     }
258 
259     // if we had created the default close button before, remove it now that we
260     // have some user-defined button
261     if ( m_impl->m_close )
262     {
263         gtk_widget_destroy(m_impl->m_close);
264         m_impl->m_close = NULL;
265     }
266 
267     GtkWidget * const button = GTKAddButton(btnid, label);
268     if ( button )
269         m_impl->m_buttons.push_back(wxInfoBarGTKImpl::Button(button, btnid));
270 }
271 
RemoveButton(wxWindowID btnid)272 void wxInfoBar::RemoveButton(wxWindowID btnid)
273 {
274     if ( !UseNative() )
275     {
276         wxInfoBarGeneric::RemoveButton(btnid);
277         return;
278     }
279 
280     // as in the generic version, look for the button starting from the end
281     wxInfoBarGTKImpl::Buttons& buttons = m_impl->m_buttons;
282     for ( wxInfoBarGTKImpl::Buttons::reverse_iterator i = buttons.rbegin();
283           i != buttons.rend();
284           ++i )
285     {
286         if (i->id == btnid)
287         {
288             gtk_widget_destroy(i->button);
289             buttons.erase(i.base());
290 
291             // see comment in GTKAddButton()
292             InvalidateBestSize();
293 
294             return;
295         }
296     }
297 
298     wxFAIL_MSG( wxString::Format("button with id %d not found", btnid) );
299 }
300 
DoApplyWidgetStyle(GtkRcStyle * style)301 void wxInfoBar::DoApplyWidgetStyle(GtkRcStyle *style)
302 {
303     wxInfoBarGeneric::DoApplyWidgetStyle(style);
304 
305     if ( UseNative() )
306         GTKApplyStyle(m_impl->m_label, style);
307 }
308 
309 #endif // wxUSE_INFOBAR
310