minor cleanup
[wxWidgets.git] / src / generic / infobar.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/infobar.cpp
3 // Purpose: generic wxInfoBar implementation
4 // Author: Vadim Zeitlin
5 // Created: 2009-07-28
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 #if wxUSE_INFOBAR
27
28 #include "wx/infobar.h"
29
30 #ifndef WX_PRECOMP
31 #include "wx/bmpbuttn.h"
32 #include "wx/button.h"
33 #include "wx/dcmemory.h"
34 #include "wx/settings.h"
35 #include "wx/statbmp.h"
36 #include "wx/stattext.h"
37 #include "wx/sizer.h"
38 #endif // WX_PRECOMP
39
40 #include "wx/artprov.h"
41 #include "wx/renderer.h"
42 #include "wx/scopeguard.h"
43
44 BEGIN_EVENT_TABLE(wxInfoBarGeneric, wxInfoBarBase)
45 EVT_BUTTON(wxID_ANY, wxInfoBarGeneric::OnButton)
46 END_EVENT_TABLE()
47
48 // ----------------------------------------------------------------------------
49 // local helpers
50 // ----------------------------------------------------------------------------
51
52 namespace
53 {
54
55 #ifdef wxHAS_DRAW_TITLE_BAR_BITMAP
56
57 wxBitmap
58 GetCloseButtonBitmap(wxWindow *win,
59 const wxSize& size,
60 const wxColour& colBg,
61 int flags = 0)
62 {
63 wxBitmap bmp(size);
64 wxMemoryDC dc(bmp);
65 dc.SetBackground(colBg);
66 dc.Clear();
67 wxRendererNative::Get().
68 DrawTitleBarBitmap(win, dc, size, wxTITLEBAR_BUTTON_CLOSE, flags);
69 return bmp;
70 }
71
72 #endif // wxHAS_DRAW_TITLE_BAR_BITMAP
73
74 } // anonymous namespace
75
76 // ============================================================================
77 // implementation
78 // ============================================================================
79
80 void wxInfoBarGeneric::Init()
81 {
82 m_icon = NULL;
83 m_text = NULL;
84 m_button = NULL;
85
86 m_showEffect =
87 m_hideEffect = wxSHOW_EFFECT_MAX;
88
89 // use default effect duration
90 m_effectDuration = 0;
91 }
92
93 bool wxInfoBarGeneric::Create(wxWindow *parent, wxWindowID winid)
94 {
95 // calling Hide() before Create() ensures that we're created initially
96 // hidden
97 Hide();
98 if ( !wxWindow::Create(parent, winid) )
99 return false;
100
101 // use special, easy to notice, colours
102 const wxColour colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK);
103 SetBackgroundColour(colBg);
104 SetOwnForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOTEXT));
105
106 // create the controls: icon, text and the button to dismiss the
107 // message.
108
109 // the icon is not shown unless it's assigned a valid bitmap
110 m_icon = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap);
111
112 m_text = new wxStaticText(this, wxID_ANY, "");
113
114 #ifdef wxHAS_DRAW_TITLE_BAR_BITMAP
115 const wxSize sizeBmp = wxArtProvider::GetSizeHint(wxART_BUTTON);
116 wxBitmap bmp = GetCloseButtonBitmap(this, sizeBmp, colBg);
117 #else // !wxHAS_DRAW_TITLE_BAR_BITMAP
118 wxBitmap bmp = wxArtProvider::GetBitmap(wxART_CLOSE, wxART_BUTTON);
119 #endif // wxHAS_DRAW_TITLE_BAR_BITMAP
120 m_button = new wxBitmapButton
121 (
122 this,
123 wxID_ANY,
124 bmp,
125 wxDefaultPosition,
126 wxDefaultSize,
127 wxBORDER_NONE
128 );
129
130 #ifdef wxHAS_DRAW_TITLE_BAR_BITMAP
131 m_button->SetBitmapPressed(
132 GetCloseButtonBitmap(this, sizeBmp, colBg, wxCONTROL_PRESSED));
133
134 m_button->SetBitmapCurrent(
135 GetCloseButtonBitmap(this, sizeBmp, colBg, wxCONTROL_CURRENT));
136 #endif // wxHAS_DRAW_TITLE_BAR_BITMAP
137
138 m_button->SetBackgroundColour(colBg);
139 m_button->SetToolTip(_("Hide this notification message."));
140
141 // center the text inside the sizer with an icon to the left of it and a
142 // button at the very right
143 //
144 // NB: AddButton() relies on the button being the last control in the sizer
145 // and being preceded by a spacer
146 wxSizer * const sizer = new wxBoxSizer(wxHORIZONTAL);
147 sizer->Add(m_icon, wxSizerFlags().Centre().Border());
148 sizer->Add(m_text, wxSizerFlags().Centre());
149 sizer->AddStretchSpacer();
150 sizer->Add(m_button, wxSizerFlags().Centre().Border());
151 SetSizer(sizer);
152
153 return true;
154 }
155
156 bool wxInfoBarGeneric::SetFont(const wxFont& font)
157 {
158 if ( !wxInfoBarBase::SetFont(font) )
159 return false;
160
161 // check that we're not called before Create()
162 if ( m_text )
163 m_text->SetFont(font);
164
165 return true;
166 }
167
168 wxInfoBarGeneric::BarPlacement wxInfoBarGeneric::GetBarPlacement() const
169 {
170 wxSizer * const sizer = GetContainingSizer();
171 if ( !sizer )
172 return BarPlacement_Unknown;
173
174 // FIXME-VC6: can't compare "const wxInfoBarGeneric *" and "wxWindow *",
175 // so need this workaround
176 wxWindow * const self = const_cast<wxInfoBarGeneric *>(this);
177 const wxSizerItemList& siblings = sizer->GetChildren();
178 if ( siblings.GetFirst()->GetData()->GetWindow() == self )
179 return BarPlacement_Top;
180 else if ( siblings.GetLast()->GetData()->GetWindow() == self )
181 return BarPlacement_Bottom;
182 else
183 return BarPlacement_Unknown;
184 }
185
186 wxShowEffect wxInfoBarGeneric::GetShowEffect() const
187 {
188 if ( m_showEffect != wxSHOW_EFFECT_MAX )
189 return m_showEffect;
190
191 switch ( GetBarPlacement() )
192 {
193 case BarPlacement_Top:
194 return wxSHOW_EFFECT_SLIDE_TO_BOTTOM;
195
196 case BarPlacement_Bottom:
197 return wxSHOW_EFFECT_SLIDE_TO_TOP;
198
199 default:
200 wxFAIL_MSG( "unknown info bar placement" );
201 // fall through
202
203 case BarPlacement_Unknown:
204 return wxSHOW_EFFECT_NONE;
205 }
206 }
207
208 wxShowEffect wxInfoBarGeneric::GetHideEffect() const
209 {
210 if ( m_hideEffect != wxSHOW_EFFECT_MAX )
211 return m_hideEffect;
212
213 switch ( GetBarPlacement() )
214 {
215 case BarPlacement_Top:
216 return wxSHOW_EFFECT_SLIDE_TO_TOP;
217
218 case BarPlacement_Bottom:
219 return wxSHOW_EFFECT_SLIDE_TO_BOTTOM;
220
221 default:
222 wxFAIL_MSG( "unknown info bar placement" );
223 // fall through
224
225 case BarPlacement_Unknown:
226 return wxSHOW_EFFECT_NONE;
227 }
228 }
229
230 void wxInfoBarGeneric::UpdateParent()
231 {
232 wxWindow * const parent = wxGetTopLevelParent(GetParent());
233 parent->Layout();
234 }
235
236 void wxInfoBarGeneric::DoHide()
237 {
238 HideWithEffect(GetHideEffect(), GetEffectDuration());
239
240 UpdateParent();
241 }
242
243 void wxInfoBarGeneric::DoShow()
244 {
245 // re-layout the parent first so that the window expands into an already
246 // unoccupied by the other controls area: for this we need to change our
247 // internal visibility flag to force Layout() to take us into account (an
248 // alternative solution to this hack would be to temporarily set
249 // wxRESERVE_SPACE_EVEN_IF_HIDDEN flag but it's not really batter)
250
251 // just change the internal flag indicating that the window is visible,
252 // without really showing it
253 wxWindowBase::Show();
254
255 // adjust the parent layout to account for us
256 UpdateParent();
257
258 // reset the flag back before really showing the window or it wouldn't be
259 // shown at all because it would believe itself already visible
260 wxWindowBase::Show(false);
261
262
263 // finally do really show the window.
264 ShowWithEffect(GetShowEffect(), GetEffectDuration());
265 }
266
267 void wxInfoBarGeneric::ShowMessage(const wxString& msg, int flags)
268 {
269 // first update the controls
270 const int icon = flags & wxICON_MASK;
271 if ( !icon || (icon == wxICON_NONE) )
272 {
273 m_icon->Hide();
274 }
275 else // do show an icon
276 {
277 m_icon->SetBitmap(wxArtProvider::GetBitmap(
278 wxArtProvider::GetMessageBoxIconId(flags),
279 wxART_BUTTON));
280 m_icon->Show();
281 }
282
283 // notice the use of EscapeMnemonics() to ensure that "&" come through
284 // correctly
285 m_text->SetLabel(wxControl::EscapeMnemonics(msg));
286
287
288 // then show this entire window if not done yet
289 if ( !IsShown() )
290 {
291 DoShow();
292 }
293 else // we're already shown
294 {
295 // just update the layout to correspond to the new message
296 Layout();
297 }
298 }
299
300 void wxInfoBarGeneric::Dismiss()
301 {
302 DoHide();
303 }
304
305 void wxInfoBarGeneric::AddButton(wxWindowID btnid, const wxString& label)
306 {
307 wxSizer * const sizer = GetSizer();
308 wxCHECK_RET( sizer, "must be created first" );
309
310 // user-added buttons replace the standard close button so remove it if we
311 // hadn't done it yet
312 if ( sizer->Detach(m_button) )
313 {
314 m_button->Hide();
315 }
316
317 wxButton * const button = new wxButton(this, btnid, label);
318
319 #ifdef __WXMAC__
320 // smaller buttons look better in the (narrow) info bar under OS X
321 button->SetWindowVariant(wxWINDOW_VARIANT_SMALL);
322 #endif // __WXMAC__
323
324 sizer->Add(button, wxSizerFlags().Centre().DoubleBorder());
325 }
326
327 void wxInfoBarGeneric::RemoveButton(wxWindowID btnid)
328 {
329 wxSizer * const sizer = GetSizer();
330 wxCHECK_RET( sizer, "must be created first" );
331
332 // iterate over the sizer items in reverse order to find the last added
333 // button with this id (ids of all buttons should be unique anyhow but if
334 // they are repeated removing the last added one probably makes more sense)
335 const wxSizerItemList& items = sizer->GetChildren();
336 for ( wxSizerItemList::compatibility_iterator node = items.GetLast();
337 node != items.GetFirst();
338 node = node->GetPrevious() )
339 {
340 const wxSizerItem * const item = node->GetData();
341
342 // if we reached the spacer separating the buttons from the text
343 // preceding them without finding our button, it must mean it's not
344 // there at all
345 if ( item->IsSpacer() )
346 {
347 wxFAIL_MSG( wxString::Format("button with id %d not found", btnid) );
348 return;
349 }
350
351 // check if we found our button
352 if ( item->GetWindow()->GetId() == btnid )
353 {
354 delete item->GetWindow();
355 break;
356 }
357 }
358
359 // check if there are any custom buttons left
360 if ( sizer->GetChildren().GetLast()->GetData()->IsSpacer() )
361 {
362 // if the last item is the spacer, none are left so restore the
363 // standard close button
364 sizer->Add(m_button, wxSizerFlags().Centre().DoubleBorder());
365 m_button->Show();
366 }
367 }
368
369 void wxInfoBarGeneric::OnButton(wxCommandEvent& WXUNUSED(event))
370 {
371 DoHide();
372 }
373
374 #endif // wxUSE_INFOBAR