Only show the default close button in wxInfoBar if there are no others.
[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 // by default, the text uses a larger, more noticeable, font
82 m_text = new wxStaticText(this, wxID_ANY, "");
83 m_text->SetFont(m_text->GetFont().Bold().Larger());
84
85 m_button = new wxBitmapButton
86 (
87 this,
88 wxID_ANY,
89 wxArtProvider::GetBitmap(wxART_CROSS_MARK),
90 wxDefaultPosition,
91 wxDefaultSize,
92 wxBORDER_NONE
93 );
94 m_button->SetToolTip(_("Hide this notification message."));
95
96 // center the text inside the sizer with an icon to the left of it and a
97 // button at the very right
98 //
99 // NB: AddButton() relies on the button being the last control in the sizer
100 // and being preceded by a spacer
101 wxSizer * const sizer = new wxBoxSizer(wxHORIZONTAL);
102 sizer->AddStretchSpacer();
103 sizer->Add(m_icon, wxSizerFlags().Centre().DoubleBorder());
104 sizer->Add(m_text, wxSizerFlags().Centre().DoubleBorder());
105 sizer->AddStretchSpacer();
106 sizer->Add(m_button, wxSizerFlags().Centre().DoubleBorder());
107 SetSizer(sizer);
108
109 return true;
110 }
111
112 bool wxInfoBarGeneric::SetFont(const wxFont& font)
113 {
114 if ( !wxInfoBarBase::SetFont(font) )
115 return false;
116
117 // check that we're not called before Create()
118 if ( m_text )
119 m_text->SetFont(font);
120
121 return true;
122 }
123
124 void wxInfoBarGeneric::UpdateParent()
125 {
126 wxWindow * const parent = wxGetTopLevelParent(GetParent());
127 parent->Layout();
128 }
129
130 void wxInfoBarGeneric::ChangeParentBackground()
131 {
132 wxWindow * const parent = GetParent();
133 m_origParentBgCol = parent->GetBackgroundColour();
134
135 wxSizer * const sizer = GetContainingSizer();
136 if ( !sizer )
137 return;
138
139 wxWindow *sibling = NULL;
140 for ( wxSizerItemList::compatibility_iterator
141 node = sizer->GetChildren().GetFirst();
142 node;
143 node = node->GetNext() )
144 {
145 if ( node->GetData()->GetWindow() == this )
146 {
147 // find the next window following us
148 for ( node = node->GetNext();
149 node;
150 node = node->GetNext() )
151 {
152 wxSizerItem * const item = node->GetData();
153 if ( item->IsWindow() )
154 {
155 sibling = item->GetWindow();
156 break;
157 }
158 }
159
160 break;
161 }
162 }
163
164 if ( sibling )
165 parent->SetOwnBackgroundColour(sibling->GetBackgroundColour());
166 }
167
168 void wxInfoBarGeneric::RestoreParentBackground()
169 {
170 GetParent()->SetOwnBackgroundColour(m_origParentBgCol);
171 }
172
173 void wxInfoBarGeneric::DoHide()
174 {
175 ChangeParentBackground();
176 wxON_BLOCK_EXIT_THIS0( wxInfoBarGeneric::RestoreParentBackground );
177
178 HideWithEffect(m_hideEffect, m_effectDuration);
179 UpdateParent();
180 }
181
182 void wxInfoBarGeneric::DoShow()
183 {
184 // re-layout the parent first so that the window expands into an already
185 // unoccupied by the other controls area: for this we need to change our
186 // internal visibility flag to force Layout() to take us into account (an
187 // alternative solution to this hack would be to temporarily set
188 // wxRESERVE_SPACE_EVEN_IF_HIDDEN flag but it's not really batter)
189
190 // just change the internal flag indicating that the window is visible,
191 // without really showing it
192 wxWindowBase::Show();
193
194 // an extra hack: we want the temporarily uncovered area in which we're
195 // going to expand to look like part of this sibling for a better effect so
196 // temporarily change the background of our parent to the same colour
197 ChangeParentBackground();
198 wxON_BLOCK_EXIT_THIS0( wxInfoBarGeneric::RestoreParentBackground );
199
200 // adjust the parent layout to account for us
201 UpdateParent();
202
203 // reset the flag back before really showing the window or it wouldn't be
204 // shown at all because it would believe itself already visible
205 wxWindowBase::Show(false);
206
207
208 // finally do really show the window.
209 ShowWithEffect(m_showEffect, m_effectDuration);
210 }
211
212 void wxInfoBarGeneric::ShowMessage(const wxString& msg, int flags)
213 {
214 // first update the controls
215 const int icon = flags & wxICON_MASK;
216 if ( !icon || (icon == wxICON_NONE) )
217 {
218 m_icon->Hide();
219 }
220 else // do show an icon
221 {
222 m_icon->SetBitmap(wxArtProvider::GetMessageBoxIcon(icon));
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::AddButton(wxWindowID btnid, const wxString& label)
244 {
245 wxSizer * const sizer = GetSizer();
246 wxCHECK_RET( sizer, "must be created first" );
247
248 // user-added buttons replace the standard close button so remove it if we
249 // hadn't done it yet
250 if ( sizer->Detach(m_button) )
251 {
252 m_button->Hide();
253 }
254
255 sizer->Add(new wxButton(this, btnid, label),
256 wxSizerFlags().Centre().DoubleBorder());
257 }
258
259 void wxInfoBarGeneric::RemoveButton(wxWindowID btnid)
260 {
261 wxSizer * const sizer = GetSizer();
262 wxCHECK_RET( sizer, "must be created first" );
263
264 // iterate over the sizer items in reverse order to find the last added
265 // button with this id (ids of all buttons should be unique anyhow but if
266 // they are repeated removing the last added one probably makes more sense)
267 const wxSizerItemList& items = sizer->GetChildren();
268 for ( wxSizerItemList::compatibility_iterator node = items.GetLast();
269 node != items.GetFirst();
270 node = node->GetPrevious() )
271 {
272 const wxSizerItem * const item = node->GetData();
273
274 // if we reached the spacer separating the buttons from the text
275 // preceding them without finding our button, it must mean it's not
276 // there at all
277 if ( item->IsSpacer() )
278 {
279 wxFAIL_MSG( wxString::Format("button with id %d not found", btnid) );
280 return;
281 }
282
283 // check if we found our button
284 if ( item->GetWindow()->GetId() == btnid )
285 {
286 delete item->GetWindow();
287 break;
288 }
289 }
290
291 // check if there are any custom buttons left
292 if ( sizer->GetChildren().GetLast()->GetData()->IsSpacer() )
293 {
294 // if the last item is the spacer, none are left so restore the
295 // standard close button
296 sizer->Add(m_button, wxSizerFlags().Centre().DoubleBorder());
297 m_button->Show();
298 }
299 }
300
301 void wxInfoBarGeneric::OnButton(wxCommandEvent& WXUNUSED(event))
302 {
303 DoHide();
304 }
305
306 #endif // wxUSE_INFOBAR