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