]>
Commit | Line | Data |
---|---|---|
1 | /////////////////////////////////////////////////////////////////////////////// | |
2 | // Name: wx/bannerwindow.h | |
3 | // Purpose: wxBannerWindow class implementation | |
4 | // Author: Vadim Zeitlin | |
5 | // Created: 2011-08-16 | |
6 | // Copyright: (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org> | |
7 | // Licence: wxWindows licence | |
8 | /////////////////////////////////////////////////////////////////////////////// | |
9 | ||
10 | #include "wx/wxprec.h" | |
11 | ||
12 | #ifdef __BORLANDC__ | |
13 | #pragma hdrstop | |
14 | #endif | |
15 | ||
16 | #if wxUSE_BANNERWINDOW | |
17 | ||
18 | #include "wx/bannerwindow.h" | |
19 | ||
20 | #ifndef WX_PRECOMP | |
21 | #include "wx/bitmap.h" | |
22 | #include "wx/colour.h" | |
23 | #endif | |
24 | ||
25 | #include "wx/dcbuffer.h" | |
26 | ||
27 | namespace | |
28 | { | |
29 | ||
30 | // Some constants for banner layout, currently they're hard coded but we could | |
31 | // easily make them configurable if needed later. | |
32 | const int MARGIN_X = 5; | |
33 | const int MARGIN_Y = 5; | |
34 | ||
35 | } // anonymous namespace | |
36 | ||
37 | const char wxBannerWindowNameStr[] = "bannerwindow"; | |
38 | ||
39 | BEGIN_EVENT_TABLE(wxBannerWindow, wxWindow) | |
40 | EVT_SIZE(wxBannerWindow::OnSize) | |
41 | EVT_PAINT(wxBannerWindow::OnPaint) | |
42 | END_EVENT_TABLE() | |
43 | ||
44 | void wxBannerWindow::Init() | |
45 | { | |
46 | m_direction = wxLEFT; | |
47 | ||
48 | m_colStart = *wxWHITE; | |
49 | m_colEnd = *wxBLUE; | |
50 | } | |
51 | ||
52 | bool | |
53 | wxBannerWindow::Create(wxWindow* parent, | |
54 | wxWindowID winid, | |
55 | wxDirection dir, | |
56 | const wxPoint& pos, | |
57 | const wxSize& size, | |
58 | long style, | |
59 | const wxString& name) | |
60 | { | |
61 | if ( !wxWindow::Create(parent, winid, pos, size, style, name) ) | |
62 | return false; | |
63 | ||
64 | wxASSERT_MSG | |
65 | ( | |
66 | dir == wxLEFT || dir == wxRIGHT || dir == wxTOP || dir == wxBOTTOM, | |
67 | wxS("Invalid banner direction") | |
68 | ); | |
69 | ||
70 | m_direction = dir; | |
71 | ||
72 | SetBackgroundStyle(wxBG_STYLE_PAINT); | |
73 | ||
74 | return true; | |
75 | } | |
76 | ||
77 | void wxBannerWindow::SetBitmap(const wxBitmap& bmp) | |
78 | { | |
79 | m_bitmap = bmp; | |
80 | ||
81 | m_colBitmapBg = wxColour(); | |
82 | ||
83 | InvalidateBestSize(); | |
84 | ||
85 | Refresh(); | |
86 | } | |
87 | ||
88 | void wxBannerWindow::SetText(const wxString& title, const wxString& message) | |
89 | { | |
90 | m_title = title; | |
91 | m_message = message; | |
92 | ||
93 | InvalidateBestSize(); | |
94 | ||
95 | Refresh(); | |
96 | } | |
97 | ||
98 | void wxBannerWindow::SetGradient(const wxColour& start, const wxColour& end) | |
99 | { | |
100 | m_colStart = start; | |
101 | m_colEnd = end; | |
102 | ||
103 | Refresh(); | |
104 | } | |
105 | ||
106 | wxFont wxBannerWindow::GetTitleFont() const | |
107 | { | |
108 | wxFont font = GetFont(); | |
109 | font.MakeBold().MakeLarger(); | |
110 | return font; | |
111 | } | |
112 | ||
113 | wxSize wxBannerWindow::DoGetBestClientSize() const | |
114 | { | |
115 | if ( m_bitmap.IsOk() ) | |
116 | { | |
117 | return m_bitmap.GetSize(); | |
118 | } | |
119 | else | |
120 | { | |
121 | wxClientDC dc(const_cast<wxBannerWindow *>(this)); | |
122 | const wxSize sizeText = dc.GetMultiLineTextExtent(m_message); | |
123 | ||
124 | dc.SetFont(GetTitleFont()); | |
125 | ||
126 | const wxSize sizeTitle = dc.GetTextExtent(m_title); | |
127 | ||
128 | wxSize sizeWin(wxMax(sizeTitle.x, sizeText.x), sizeTitle.y + sizeText.y); | |
129 | ||
130 | // If we draw the text vertically width and height are swapped. | |
131 | if ( m_direction == wxLEFT || m_direction == wxRIGHT ) | |
132 | wxSwap(sizeWin.x, sizeWin.y); | |
133 | ||
134 | sizeWin += 2*wxSize(MARGIN_X, MARGIN_Y); | |
135 | ||
136 | return sizeWin; | |
137 | } | |
138 | } | |
139 | ||
140 | void wxBannerWindow::OnSize(wxSizeEvent& event) | |
141 | { | |
142 | Refresh(); | |
143 | ||
144 | event.Skip(); | |
145 | } | |
146 | ||
147 | void wxBannerWindow::OnPaint(wxPaintEvent& WXUNUSED(event)) | |
148 | { | |
149 | if ( m_bitmap.IsOk() && m_title.empty() && m_message.empty() ) | |
150 | { | |
151 | // No need for buffering in this case. | |
152 | wxPaintDC dc(this); | |
153 | ||
154 | DrawBitmapBackground(dc); | |
155 | } | |
156 | else // We need to compose our contents ourselves. | |
157 | { | |
158 | wxAutoBufferedPaintDC dc(this); | |
159 | ||
160 | // Deal with the background first. | |
161 | if ( m_bitmap.IsOk() ) | |
162 | { | |
163 | DrawBitmapBackground(dc); | |
164 | } | |
165 | else // Draw gradient background. | |
166 | { | |
167 | wxDirection gradientDir; | |
168 | if ( m_direction == wxLEFT ) | |
169 | { | |
170 | gradientDir = wxTOP; | |
171 | } | |
172 | else if ( m_direction == wxRIGHT ) | |
173 | { | |
174 | gradientDir = wxBOTTOM; | |
175 | } | |
176 | else // For both wxTOP and wxBOTTOM. | |
177 | { | |
178 | gradientDir = wxRIGHT; | |
179 | } | |
180 | ||
181 | dc.GradientFillLinear(GetClientRect(), m_colStart, m_colEnd, | |
182 | gradientDir); | |
183 | } | |
184 | ||
185 | // Now draw the text on top of it. | |
186 | dc.SetFont(GetTitleFont()); | |
187 | ||
188 | wxPoint pos(MARGIN_X, MARGIN_Y); | |
189 | DrawBannerTextLine(dc, m_title, pos); | |
190 | pos.y += dc.GetTextExtent(m_title).y; | |
191 | ||
192 | dc.SetFont(GetFont()); | |
193 | ||
194 | wxArrayString lines = wxSplit(m_message, '\n', '\0'); | |
195 | const unsigned numLines = lines.size(); | |
196 | for ( unsigned n = 0; n < numLines; n++ ) | |
197 | { | |
198 | const wxString& line = lines[n]; | |
199 | ||
200 | DrawBannerTextLine(dc, line, pos); | |
201 | pos.y += dc.GetTextExtent(line).y; | |
202 | } | |
203 | } | |
204 | } | |
205 | ||
206 | wxColour wxBannerWindow::GetBitmapBg() | |
207 | { | |
208 | if ( m_colBitmapBg.IsOk() ) | |
209 | return m_colBitmapBg; | |
210 | ||
211 | // Determine the colour to use to extend the bitmap. It's the colour of the | |
212 | // bitmap pixels at the edge closest to the area where it can be extended. | |
213 | wxImage image(m_bitmap.ConvertToImage()); | |
214 | ||
215 | // The point we get the colour from. The choice is arbitrary and in general | |
216 | // the bitmap should have the same colour on the entire edge of this point | |
217 | // for extending it to look good. | |
218 | wxPoint p; | |
219 | ||
220 | wxSize size = image.GetSize(); | |
221 | size.x--; | |
222 | size.y--; | |
223 | ||
224 | switch ( m_direction ) | |
225 | { | |
226 | case wxTOP: | |
227 | case wxBOTTOM: | |
228 | // The bitmap will be extended to the right. | |
229 | p.x = size.x; | |
230 | p.y = 0; | |
231 | break; | |
232 | ||
233 | case wxLEFT: | |
234 | // The bitmap will be extended from the top. | |
235 | p.x = 0; | |
236 | p.y = 0; | |
237 | break; | |
238 | ||
239 | case wxRIGHT: | |
240 | // The bitmap will be extended to the bottom. | |
241 | p.x = 0; | |
242 | p.y = size.y; | |
243 | break; | |
244 | ||
245 | // This case is there only to prevent g++ warnings about not handling | |
246 | // some enum elements in the switch, it can't really happen. | |
247 | case wxALL: | |
248 | wxFAIL_MSG( wxS("Unreachable") ); | |
249 | } | |
250 | ||
251 | m_colBitmapBg.Set(image.GetRed(p.x, p.y), | |
252 | image.GetGreen(p.x, p.y), | |
253 | image.GetBlue(p.x, p.y)); | |
254 | ||
255 | return m_colBitmapBg; | |
256 | } | |
257 | ||
258 | void wxBannerWindow::DrawBitmapBackground(wxDC& dc) | |
259 | { | |
260 | // We may need to fill the part of the background not covered by the bitmap | |
261 | // with the solid colour extending the bitmap, this rectangle will hold the | |
262 | // area to be filled (which could be empty if the bitmap is big enough). | |
263 | wxRect rectSolid; | |
264 | ||
265 | const wxSize size = GetClientSize(); | |
266 | ||
267 | switch ( m_direction ) | |
268 | { | |
269 | case wxTOP: | |
270 | case wxBOTTOM: | |
271 | // Draw the bitmap at the origin, its rightmost could be truncated, | |
272 | // as it's meant to be. | |
273 | dc.DrawBitmap(m_bitmap, 0, 0); | |
274 | ||
275 | rectSolid.x = m_bitmap.GetWidth(); | |
276 | rectSolid.width = size.x - rectSolid.x; | |
277 | rectSolid.height = size.y; | |
278 | break; | |
279 | ||
280 | case wxLEFT: | |
281 | // The top most part of the bitmap may be truncated but its bottom | |
282 | // must be always visible so intentionally draw it possibly partly | |
283 | // outside of the window. | |
284 | rectSolid.width = size.x; | |
285 | rectSolid.height = size.y - m_bitmap.GetHeight(); | |
286 | dc.DrawBitmap(m_bitmap, 0, rectSolid.height); | |
287 | break; | |
288 | ||
289 | case wxRIGHT: | |
290 | // Draw the bitmap at the origin, possibly truncating its | |
291 | // bottommost part. | |
292 | dc.DrawBitmap(m_bitmap, 0, 0); | |
293 | ||
294 | rectSolid.y = m_bitmap.GetHeight(); | |
295 | rectSolid.height = size.y - rectSolid.y; | |
296 | rectSolid.width = size.x; | |
297 | break; | |
298 | ||
299 | // This case is there only to prevent g++ warnings about not handling | |
300 | // some enum elements in the switch, it can't really happen. | |
301 | case wxALL: | |
302 | wxFAIL_MSG( wxS("Unreachable") ); | |
303 | } | |
304 | ||
305 | if ( rectSolid.width > 0 && rectSolid.height > 0 ) | |
306 | { | |
307 | dc.SetPen(*wxTRANSPARENT_PEN); | |
308 | dc.SetBrush(GetBitmapBg()); | |
309 | dc.DrawRectangle(rectSolid); | |
310 | } | |
311 | } | |
312 | ||
313 | void | |
314 | wxBannerWindow::DrawBannerTextLine(wxDC& dc, | |
315 | const wxString& str, | |
316 | const wxPoint& pos) | |
317 | { | |
318 | switch ( m_direction ) | |
319 | { | |
320 | case wxTOP: | |
321 | case wxBOTTOM: | |
322 | // The simple case: we just draw the text normally. | |
323 | dc.DrawText(str, pos); | |
324 | break; | |
325 | ||
326 | case wxLEFT: | |
327 | // We draw the text vertically and start from the lower left | |
328 | // corner and not the upper left one as usual. | |
329 | dc.DrawRotatedText(str, pos.y, GetClientSize().y - pos.x, 90); | |
330 | break; | |
331 | ||
332 | case wxRIGHT: | |
333 | // We also draw the text vertically but now we start from the upper | |
334 | // right corner and draw it from top to bottom. | |
335 | dc.DrawRotatedText(str, GetClientSize().x - pos.y, pos.x, -90); | |
336 | break; | |
337 | ||
338 | case wxALL: | |
339 | wxFAIL_MSG( wxS("Unreachable") ); | |
340 | } | |
341 | } | |
342 | ||
343 | #endif // wxUSE_BANNERWINDOW |