]> git.saurik.com Git - wxWidgets.git/blob - src/univ/ctrlrend.cpp
fix Get(Sub)ItemRect() after changes of r54437; added test for it (see #10175)
[wxWidgets.git] / src / univ / ctrlrend.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/univ/ctrlrend.cpp
3 // Purpose: wxControlRenderer implementation
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 15.08.00
7 // RCS-ID: $Id$
8 // Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #ifndef WX_PRECOMP
28 #include "wx/app.h"
29 #include "wx/control.h"
30 #include "wx/checklst.h"
31 #include "wx/listbox.h"
32 #include "wx/scrolbar.h"
33 #include "wx/dc.h"
34 #include "wx/log.h"
35 #include "wx/gauge.h"
36 #include "wx/image.h"
37 #endif // WX_PRECOMP
38
39 #include "wx/univ/theme.h"
40 #include "wx/univ/renderer.h"
41 #include "wx/univ/colschem.h"
42
43 // ============================================================================
44 // implementation
45 // ============================================================================
46
47 wxRenderer::~wxRenderer()
48 {
49 }
50
51 // ----------------------------------------------------------------------------
52 // wxControlRenderer
53 // ----------------------------------------------------------------------------
54
55 wxControlRenderer::wxControlRenderer(wxWindow *window,
56 wxDC& dc,
57 wxRenderer *renderer)
58 : m_dc(dc)
59 {
60 m_window = window;
61 m_renderer = renderer;
62
63 wxSize size = m_window->GetClientSize();
64 m_rect.x =
65 m_rect.y = 0;
66 m_rect.width = size.x;
67 m_rect.height = size.y;
68 }
69
70 void wxControlRenderer::DrawLabel(const wxBitmap& bitmap,
71 wxCoord marginX, wxCoord marginY)
72 {
73 m_dc.SetBackgroundMode(wxTRANSPARENT);
74 m_dc.SetFont(m_window->GetFont());
75 m_dc.SetTextForeground(m_window->GetForegroundColour());
76
77 wxString label = m_window->GetLabel();
78 if ( !label.empty() || bitmap.Ok() )
79 {
80 wxRect rectLabel = m_rect;
81 if ( bitmap.Ok() )
82 {
83 rectLabel.Inflate(-marginX, -marginY);
84 }
85
86 wxControl *ctrl = wxStaticCast(m_window, wxControl);
87
88 m_renderer->DrawButtonLabel(m_dc,
89 label,
90 bitmap,
91 rectLabel,
92 m_window->GetStateFlags(),
93 ctrl->GetAlignment(),
94 ctrl->GetAccelIndex());
95 }
96 }
97
98 void wxControlRenderer::DrawFrame()
99 {
100 m_dc.SetFont(m_window->GetFont());
101 m_dc.SetTextForeground(m_window->GetForegroundColour());
102 m_dc.SetTextBackground(m_window->GetBackgroundColour());
103
104 wxControl *ctrl = wxStaticCast(m_window, wxControl);
105
106 m_renderer->DrawFrame(m_dc,
107 m_window->GetLabel(),
108 m_rect,
109 m_window->GetStateFlags(),
110 ctrl->GetAlignment(),
111 ctrl->GetAccelIndex());
112 }
113
114 void wxControlRenderer::DrawButtonBorder()
115 {
116 int flags = m_window->GetStateFlags();
117
118 m_renderer->DrawButtonBorder(m_dc, m_rect, flags, &m_rect);
119
120 // Why do this here?
121 // m_renderer->DrawButtonSurface(m_dc, wxTHEME_BG_COLOUR(m_window), m_rect, flags );
122 }
123
124 void wxControlRenderer::DrawBitmap(const wxBitmap& bitmap)
125 {
126 int style = m_window->GetWindowStyle();
127 DrawBitmap(m_dc, bitmap, m_rect,
128 style & wxALIGN_MASK,
129 style & wxBI_EXPAND ? wxEXPAND : wxSTRETCH_NOT);
130 }
131
132 /* static */
133 void wxControlRenderer::DrawBitmap(wxDC &dc,
134 const wxBitmap& bitmap,
135 const wxRect& rect,
136 int alignment,
137 wxStretch stretch)
138 {
139 // we may change the bitmap if we stretch it
140 wxBitmap bmp = bitmap;
141 if ( !bmp.Ok() )
142 return;
143
144 int width = bmp.GetWidth(),
145 height = bmp.GetHeight();
146
147 wxCoord x = 0,
148 y = 0;
149 if ( stretch & wxTILE )
150 {
151 // tile the bitmap
152 for ( ; x < rect.width; x += width )
153 {
154 for ( y = 0; y < rect.height; y += height )
155 {
156 // no need to use mask here as we cover the entire window area
157 dc.DrawBitmap(bmp, x, y);
158 }
159 }
160 }
161 #if wxUSE_IMAGE
162 else if ( stretch & wxEXPAND )
163 {
164 // stretch bitmap to fill the entire control
165 bmp = wxBitmap(wxImage(bmp.ConvertToImage()).Scale(rect.width, rect.height));
166 }
167 #endif // wxUSE_IMAGE
168 else // not stretched, not tiled
169 {
170 if ( alignment & wxALIGN_RIGHT )
171 {
172 x = rect.GetRight() - width;
173 }
174 else if ( alignment & wxALIGN_CENTRE )
175 {
176 x = (rect.GetLeft() + rect.GetRight() - width + 1) / 2;
177 }
178 else // alignment & wxALIGN_LEFT
179 {
180 x = rect.GetLeft();
181 }
182
183 if ( alignment & wxALIGN_BOTTOM )
184 {
185 y = rect.GetBottom() - height;
186 }
187 else if ( alignment & wxALIGN_CENTRE_VERTICAL )
188 {
189 y = (rect.GetTop() + rect.GetBottom() - height + 1) / 2;
190 }
191 else // alignment & wxALIGN_TOP
192 {
193 y = rect.GetTop();
194 }
195 }
196
197 // do draw it
198 dc.DrawBitmap(bmp, x, y, true /* use mask */);
199 }
200
201 #if wxUSE_SCROLLBAR
202
203 void wxControlRenderer::DrawScrollbar(const wxScrollBar *scrollbar,
204 int WXUNUSED(thumbPosOld))
205 {
206 // we will only redraw the parts which must be redrawn and not everything
207 wxRegion rgnUpdate = scrollbar->GetUpdateRegion();
208
209 {
210 wxRect rectUpdate = rgnUpdate.GetBox();
211 wxLogTrace(_T("scrollbar"),
212 _T("%s redraw: update box is (%d, %d)-(%d, %d)"),
213 scrollbar->IsVertical() ? _T("vert") : _T("horz"),
214 rectUpdate.GetLeft(),
215 rectUpdate.GetTop(),
216 rectUpdate.GetRight(),
217 rectUpdate.GetBottom());
218
219 #if 0 //def WXDEBUG_SCROLLBAR
220 static bool s_refreshDebug = false;
221 if ( s_refreshDebug )
222 {
223 wxClientDC dc(wxConstCast(scrollbar, wxScrollBar));
224 dc.SetBrush(*wxRED_BRUSH);
225 dc.SetPen(*wxTRANSPARENT_PEN);
226 dc.DrawRectangle(rectUpdate);
227
228 // under Unix we use "--sync" X option for this
229 #ifdef __WXMSW__
230 ::GdiFlush();
231 ::Sleep(200);
232 #endif // __WXMSW__
233 }
234 #endif // WXDEBUG_SCROLLBAR
235 }
236
237 wxOrientation orient = scrollbar->IsVertical() ? wxVERTICAL
238 : wxHORIZONTAL;
239
240 // the shaft
241 for ( int nBar = 0; nBar < 2; nBar++ )
242 {
243 wxScrollBar::Element elem =
244 (wxScrollBar::Element)(wxScrollBar::Element_Bar_1 + nBar);
245
246 wxRect rectBar = scrollbar->GetScrollbarRect(elem);
247
248 if ( rgnUpdate.Contains(rectBar) )
249 {
250 wxLogTrace(_T("scrollbar"),
251 _T("drawing bar part %d at (%d, %d)-(%d, %d)"),
252 nBar + 1,
253 rectBar.GetLeft(),
254 rectBar.GetTop(),
255 rectBar.GetRight(),
256 rectBar.GetBottom());
257
258 m_renderer->DrawScrollbarShaft(m_dc,
259 orient,
260 rectBar,
261 scrollbar->GetState(elem));
262 }
263 }
264
265 // arrows
266 for ( int nArrow = 0; nArrow < 2; nArrow++ )
267 {
268 wxScrollBar::Element elem =
269 (wxScrollBar::Element)(wxScrollBar::Element_Arrow_Line_1 + nArrow);
270
271 wxRect rectArrow = scrollbar->GetScrollbarRect(elem);
272 if ( rgnUpdate.Contains(rectArrow) )
273 {
274 wxLogTrace(_T("scrollbar"),
275 _T("drawing arrow %d at (%d, %d)-(%d, %d)"),
276 nArrow + 1,
277 rectArrow.GetLeft(),
278 rectArrow.GetTop(),
279 rectArrow.GetRight(),
280 rectArrow.GetBottom());
281
282 scrollbar->GetArrows().DrawArrow
283 (
284 (wxScrollArrows::Arrow)nArrow,
285 m_dc,
286 rectArrow,
287 true // draw a scrollbar arrow, not just an arrow
288 );
289 }
290 }
291
292 // TODO: support for page arrows
293
294 // and the thumb
295 wxScrollBar::Element elem = wxScrollBar::Element_Thumb;
296 wxRect rectThumb = scrollbar->GetScrollbarRect(elem);
297 if ( rectThumb.width && rectThumb.height && rgnUpdate.Contains(rectThumb) )
298 {
299 wxLogTrace(_T("scrollbar"),
300 _T("drawing thumb at (%d, %d)-(%d, %d)"),
301 rectThumb.GetLeft(),
302 rectThumb.GetTop(),
303 rectThumb.GetRight(),
304 rectThumb.GetBottom());
305
306 m_renderer->DrawScrollbarThumb(m_dc,
307 orient,
308 rectThumb,
309 scrollbar->GetState(elem));
310 }
311 }
312
313 #endif // wxUSE_SCROLLBAR
314
315 void wxControlRenderer::DrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
316 {
317 wxASSERT_MSG( x1 == x2 || y1 == y2,
318 _T("line must be either horizontal or vertical") );
319
320 if ( x1 == x2 )
321 m_renderer->DrawVerticalLine(m_dc, x1, y1, y2);
322 else // horizontal
323 m_renderer->DrawHorizontalLine(m_dc, y1, x1, x2);
324 }
325
326 #if wxUSE_LISTBOX
327
328 void wxControlRenderer::DrawItems(const wxListBox *lbox,
329 size_t itemFirst, size_t itemLast)
330 {
331 DoDrawItems(lbox, itemFirst, itemLast);
332 }
333
334 void wxControlRenderer::DoDrawItems(const wxListBox *lbox,
335 size_t itemFirst, size_t itemLast,
336 #if wxUSE_CHECKLISTBOX
337 bool isCheckLbox
338 #else
339 bool WXUNUSED(isCheckLbox)
340 #endif
341 )
342 {
343 // prepare for the drawing: calc the initial position
344 wxCoord lineHeight = lbox->GetLineHeight();
345
346 // note that SetClippingRegion() needs the physical (unscrolled)
347 // coordinates while we use the logical (scrolled) ones for the drawing
348 // itself
349 wxRect rect;
350 wxSize size = lbox->GetClientSize();
351 rect.width = size.x;
352 rect.height = size.y;
353
354 // keep the text inside the client rect or we will overwrite the vertical
355 // scrollbar for the long strings
356 m_dc.SetClippingRegion(rect.x, rect.y, rect.width + 1, rect.height + 1);
357
358 // adjust the rect position now
359 lbox->CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y);
360 rect.y += itemFirst*lineHeight;
361 rect.height = lineHeight;
362
363 // the rect should go to the right visible border so adjust the width if x
364 // is shifted (rightmost point should stay the same)
365 rect.width -= rect.x;
366
367 // we'll keep the text colour unchanged
368 m_dc.SetTextForeground(lbox->GetForegroundColour());
369
370 // an item should have the focused rect only when the lbox has focus, so
371 // make sure that we never set wxCONTROL_FOCUSED flag if it doesn't
372 int itemCurrent = wxWindow::FindFocus() == (wxWindow *)lbox // cast needed
373 ? lbox->GetCurrentItem()
374 : -1;
375 for ( size_t n = itemFirst; n < itemLast; n++ )
376 {
377 int flags = 0;
378 if ( (int)n == itemCurrent )
379 flags |= wxCONTROL_FOCUSED;
380 if ( lbox->IsSelected(n) )
381 flags |= wxCONTROL_SELECTED;
382
383 #if wxUSE_CHECKLISTBOX
384 if ( isCheckLbox )
385 {
386 wxCheckListBox *checklstbox = wxStaticCast(lbox, wxCheckListBox);
387 if ( checklstbox->IsChecked(n) )
388 flags |= wxCONTROL_CHECKED;
389
390 m_renderer->DrawCheckItem(m_dc, lbox->GetString(n),
391 wxNullBitmap,
392 rect,
393 flags);
394 }
395 else
396 #endif // wxUSE_CHECKLISTBOX
397 {
398 m_renderer->DrawItem(m_dc, lbox->GetString(n), rect, flags);
399 }
400
401 rect.y += lineHeight;
402 }
403 }
404
405 #endif // wxUSE_LISTBOX
406
407 #if wxUSE_CHECKLISTBOX
408
409 void wxControlRenderer::DrawCheckItems(const wxCheckListBox *lbox,
410 size_t itemFirst, size_t itemLast)
411 {
412 DoDrawItems(lbox, itemFirst, itemLast, true);
413 }
414
415 #endif // wxUSE_CHECKLISTBOX
416
417 #if wxUSE_GAUGE
418
419 void wxControlRenderer::DrawProgressBar(const wxGauge *gauge)
420 {
421 // draw background
422 m_dc.SetBrush(wxBrush(m_window->GetBackgroundColour(), wxSOLID));
423 m_dc.SetPen(*wxTRANSPARENT_PEN);
424 m_dc.DrawRectangle(m_rect);
425
426 int max = gauge->GetRange();
427 if ( !max )
428 {
429 // nothing to draw
430 return;
431 }
432
433 // calc the filled rect
434 int pos = gauge->GetValue();
435 int left = max - pos;
436
437 wxRect rect = m_rect;
438 rect.Deflate(1); // FIXME this depends on the border width
439
440 wxColour col = m_window->UseFgCol() ? m_window->GetForegroundColour()
441 : wxTHEME_COLOUR(GAUGE);
442 m_dc.SetBrush(wxBrush(col, wxSOLID));
443
444 if ( gauge->IsSmooth() )
445 {
446 // just draw the rectangle in one go
447 if ( gauge->IsVertical() )
448 {
449 // vert bars grow from bottom to top
450 wxCoord dy = ((rect.height - 1) * left) / max;
451 rect.y += dy;
452 rect.height -= dy;
453 }
454 else // horizontal
455 {
456 // grow from left to right
457 rect.width -= ((rect.width - 1) * left) / max;
458 }
459
460 m_dc.DrawRectangle(rect);
461 }
462 else // discrete
463 {
464 wxSize sizeStep = m_renderer->GetProgressBarStep();
465 int step = gauge->IsVertical() ? sizeStep.y : sizeStep.x;
466
467 // we divide by it below!
468 wxCHECK_RET( step, _T("invalid wxGauge step") );
469
470 // round up to make the progress appear to start faster
471 int lenTotal = gauge->IsVertical() ? rect.height : rect.width;
472 int steps = ((lenTotal + step - 1) * pos) / (max * step);
473
474 // calc the coords of one small rect
475 wxCoord *px;
476 wxCoord dx, dy;
477 if ( gauge->IsVertical() )
478 {
479 // draw from bottom to top: so first set y to the bottom
480 rect.y += rect.height - 1;
481
482 // then adjust the height
483 rect.height = step;
484
485 // and then adjust y again to be what it should for the first rect
486 rect.y -= rect.height;
487
488 // we are going up
489 step = -step;
490
491 // remember that this will be the coord which will change
492 px = &rect.y;
493
494 dy = 1;
495 dx = 0;
496 }
497 else // horizontal
498 {
499 // don't leave 2 empty pixels in the beginning
500 rect.x--;
501
502 px = &rect.x;
503 rect.width = step;
504
505 dy = 0;
506 dx = 1;
507 }
508
509 for ( int n = 0; n < steps; n++ )
510 {
511 wxRect rectSegment = rect;
512 rectSegment.Deflate(dx, dy);
513
514 m_dc.DrawRectangle(rectSegment);
515
516 *px += step;
517 if ( *px < 1 )
518 {
519 // this can only happen for the last step of vertical gauge
520 rect.height = *px - step - 1;
521 *px = 1;
522 }
523 else if ( *px > lenTotal - step )
524 {
525 // this can only happen for the last step of horizontal gauge
526 rect.width = lenTotal - *px - 1;
527 }
528 }
529 }
530 }
531
532 #endif // wxUSE_GAUGE