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