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