Added wxInfoBar::Dismiss().
[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: wxhead.cpp,v 1.10 2009-06-29 10:23:04 zeitlin Exp $
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 #ifndef WX_PRECOMP
29 #include "wx/bmpbuttn.h"
30 #include "wx/button.h"
31 #include "wx/settings.h"
32 #include "wx/statbmp.h"
33 #include "wx/stattext.h"
34 #endif // WX_PRECOMP
35
36 #include "wx/infobar.h"
37
38 #include "wx/artprov.h"
39 #include "wx/scopeguard.h"
40 #include "wx/sizer.h"
41
42 BEGIN_EVENT_TABLE(wxInfoBarGeneric, wxInfoBarBase)
43 EVT_BUTTON(wxID_ANY, wxInfoBarGeneric::OnButton)
44 END_EVENT_TABLE()
45
46 // ============================================================================
47 // implementation
48 // ============================================================================
49
50 void wxInfoBarGeneric::Init()
51 {
52 m_icon = NULL;
53 m_text = NULL;
54 m_button = NULL;
55
56 m_showEffect = wxSHOW_EFFECT_SLIDE_TO_BOTTOM;
57 m_hideEffect = wxSHOW_EFFECT_SLIDE_TO_TOP;
58
59 // use default effect duration
60 m_effectDuration = 0;
61 }
62
63 bool wxInfoBarGeneric::Create(wxWindow *parent, wxWindowID winid)
64 {
65 // calling Hide() before Create() ensures that we're created initially
66 // hidden
67 Hide();
68 if ( !wxWindow::Create(parent, winid) )
69 return false;
70
71 // use special, easy to notice, colours
72 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK));
73 SetOwnForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOTEXT));
74
75 // create the controls: icon, text and the button to dismiss the
76 // message.
77
78 // the icon is not shown unless it's assigned a valid bitmap
79 m_icon = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap);
80
81 m_text = new wxStaticText(this, wxID_ANY, "");
82
83 m_button = new wxBitmapButton
84 (
85 this,
86 wxID_ANY,
87 wxArtProvider::GetBitmap(wxART_CLOSE, wxART_MENU),
88 wxDefaultPosition,
89 wxDefaultSize,
90 wxBORDER_NONE
91 );
92 m_button->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK));
93 m_button->SetToolTip(_("Hide this notification message."));
94
95 // center the text inside the sizer with an icon to the left of it and a
96 // button at the very right
97 //
98 // NB: AddButton() relies on the button being the last control in the sizer
99 // and being preceded by a spacer
100 wxSizer * const sizer = new wxBoxSizer(wxHORIZONTAL);
101 sizer->Add(m_icon, wxSizerFlags().Centre().Border());
102 sizer->Add(m_text, wxSizerFlags().Centre());
103 sizer->AddStretchSpacer();
104 sizer->Add(m_button, wxSizerFlags().Centre().Border());
105 SetSizer(sizer);
106
107 return true;
108 }
109
110 bool wxInfoBarGeneric::SetFont(const wxFont& font)
111 {
112 if ( !wxInfoBarBase::SetFont(font) )
113 return false;
114
115 // check that we're not called before Create()
116 if ( m_text )
117 m_text->SetFont(font);
118
119 return true;
120 }
121
122 void wxInfoBarGeneric::UpdateParent()
123 {
124 wxWindow * const parent = wxGetTopLevelParent(GetParent());
125 parent->Layout();
126 }
127
128 void wxInfoBarGeneric::ChangeParentBackground()
129 {
130 wxWindow * const parent = GetParent();
131 m_origParentBgCol = parent->GetBackgroundColour();
132
133 wxSizer * const sizer = GetContainingSizer();
134 if ( !sizer )
135 return;
136
137 wxWindow *sibling = NULL;
138 for ( wxSizerItemList::compatibility_iterator
139 node = sizer->GetChildren().GetFirst();
140 node;
141 node = node->GetNext() )
142 {
143 if ( node->GetData()->GetWindow() == this )
144 {
145 // find the next window following us
146 for ( node = node->GetNext();
147 node;
148 node = node->GetNext() )
149 {
150 wxSizerItem * const item = node->GetData();
151 if ( item->IsWindow() )
152 {
153 sibling = item->GetWindow();
154 break;
155 }
156 }
157
158 break;
159 }
160 }
161
162 if ( sibling )
163 parent->SetOwnBackgroundColour(sibling->GetBackgroundColour());
164 }
165
166 void wxInfoBarGeneric::RestoreParentBackground()
167 {
168 GetParent()->SetOwnBackgroundColour(m_origParentBgCol);
169 }
170
171 void wxInfoBarGeneric::DoHide()
172 {
173 ChangeParentBackground();
174 wxON_BLOCK_EXIT_THIS0( wxInfoBarGeneric::RestoreParentBackground );
175
176 HideWithEffect(m_hideEffect, m_effectDuration);
177 UpdateParent();
178 }
179
180 void wxInfoBarGeneric::DoShow()
181 {
182 // re-layout the parent first so that the window expands into an already
183 // unoccupied by the other controls area: for this we need to change our
184 // internal visibility flag to force Layout() to take us into account (an
185 // alternative solution to this hack would be to temporarily set
186 // wxRESERVE_SPACE_EVEN_IF_HIDDEN flag but it's not really batter)
187
188 // just change the internal flag indicating that the window is visible,
189 // without really showing it
190 wxWindowBase::Show();
191
192 // an extra hack: we want the temporarily uncovered area in which we're
193 // going to expand to look like part of this sibling for a better effect so
194 // temporarily change the background of our parent to the same colour
195 ChangeParentBackground();
196 wxON_BLOCK_EXIT_THIS0( wxInfoBarGeneric::RestoreParentBackground );
197
198 // adjust the parent layout to account for us
199 UpdateParent();
200
201 // reset the flag back before really showing the window or it wouldn't be
202 // shown at all because it would believe itself already visible
203 wxWindowBase::Show(false);
204
205
206 // finally do really show the window.
207 ShowWithEffect(m_showEffect, m_effectDuration);
208 }
209
210 void wxInfoBarGeneric::ShowMessage(const wxString& msg, int flags)
211 {
212 // first update the controls
213 const int icon = flags & wxICON_MASK;
214 if ( !icon || (icon == wxICON_NONE) )
215 {
216 m_icon->Hide();
217 }
218 else // do show an icon
219 {
220 m_icon->SetBitmap(wxArtProvider::GetBitmap(
221 wxArtProvider::GetMessageBoxIconId(flags),
222 wxART_MENU));
223 m_icon->Show();
224 }
225
226 // notice the use of EscapeMnemonics() to ensure that "&" come through
227 // correctly
228 m_text->SetLabel(wxControl::EscapeMnemonics(msg));
229
230
231 // then show this entire window if not done yet
232 if ( !IsShown() )
233 {
234 DoShow();
235 }
236 else // we're already shown
237 {
238 // just update the layout to correspond to the new message
239 Layout();
240 }
241 }
242
243 void wxInfoBarGeneric::Dismiss()
244 {
245 DoHide();
246 }
247
248 void wxInfoBarGeneric::AddButton(wxWindowID btnid, const wxString& label)
249 {
250 wxSizer * const sizer = GetSizer();
251 wxCHECK_RET( sizer, "must be created first" );
252
253 // user-added buttons replace the standard close button so remove it if we
254 // hadn't done it yet
255 if ( sizer->Detach(m_button) )
256 {
257 m_button->Hide();
258 }
259
260 sizer->Add(new wxButton(this, btnid, label),
261 wxSizerFlags().Centre().DoubleBorder());
262 }
263
264 void wxInfoBarGeneric::RemoveButton(wxWindowID btnid)
265 {
266 wxSizer * const sizer = GetSizer();
267 wxCHECK_RET( sizer, "must be created first" );
268
269 // iterate over the sizer items in reverse order to find the last added
270 // button with this id (ids of all buttons should be unique anyhow but if
271 // they are repeated removing the last added one probably makes more sense)
272 const wxSizerItemList& items = sizer->GetChildren();
273 for ( wxSizerItemList::compatibility_iterator node = items.GetLast();
274 node != items.GetFirst();
275 node = node->GetPrevious() )
276 {
277 const wxSizerItem * const item = node->GetData();
278
279 // if we reached the spacer separating the buttons from the text
280 // preceding them without finding our button, it must mean it's not
281 // there at all
282 if ( item->IsSpacer() )
283 {
284 wxFAIL_MSG( wxString::Format("button with id %d not found", btnid) );
285 return;
286 }
287
288 // check if we found our button
289 if ( item->GetWindow()->GetId() == btnid )
290 {
291 delete item->GetWindow();
292 break;
293 }
294 }
295
296 // check if there are any custom buttons left
297 if ( sizer->GetChildren().GetLast()->GetData()->IsSpacer() )
298 {
299 // if the last item is the spacer, none are left so restore the
300 // standard close button
301 sizer->Add(m_button, wxSizerFlags().Centre().DoubleBorder());
302 m_button->Show();
303 }
304 }
305
306 void wxInfoBarGeneric::OnButton(wxCommandEvent& WXUNUSED(event))
307 {
308 DoHide();
309 }
310
311 #endif // wxUSE_INFOBAR