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