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