]> git.saurik.com Git - wxWidgets.git/blame - src/univ/ctrlrend.cpp
preserve type when loaded image is rescaled, #11543
[wxWidgets.git] / src / univ / ctrlrend.cpp
CommitLineData
1e6feb95 1///////////////////////////////////////////////////////////////////////////////
e4db172a 2// Name: src/univ/ctrlrend.cpp
1e6feb95
VZ
3// Purpose: wxControlRenderer implementation
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 15.08.00
7// RCS-ID: $Id$
442b35b5 8// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
65571936 9// Licence: wxWindows licence
1e6feb95
VZ
10///////////////////////////////////////////////////////////////////////////////
11
12// ===========================================================================
13// declarations
14// ===========================================================================
15
16// ---------------------------------------------------------------------------
17// headers
18// ---------------------------------------------------------------------------
19
1e6feb95
VZ
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"
e4db172a 34 #include "wx/log.h"
9d2c19f1 35 #include "wx/gauge.h"
155ecd4c 36 #include "wx/image.h"
1e6feb95
VZ
37#endif // WX_PRECOMP
38
1e6feb95
VZ
39#include "wx/univ/theme.h"
40#include "wx/univ/renderer.h"
41#include "wx/univ/colschem.h"
42
43// ============================================================================
44// implementation
45// ============================================================================
46
1e6feb95
VZ
47wxRenderer::~wxRenderer()
48{
49}
50
51// ----------------------------------------------------------------------------
52// wxControlRenderer
53// ----------------------------------------------------------------------------
54
55wxControlRenderer::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
fdc468a0
VZ
70void 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
90void wxControlRenderer::DrawButtonLabel(const wxBitmap& bitmap,
91 wxCoord marginX, wxCoord marginY)
1e6feb95 92{
3a0b23eb 93 m_dc.SetBackgroundMode(wxTRANSPARENT);
1e6feb95
VZ
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.Ok() )
99 {
100 wxRect rectLabel = m_rect;
101 if ( bitmap.Ok() )
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
118void 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
134void wxControlRenderer::DrawButtonBorder()
135{
136 int flags = m_window->GetStateFlags();
137
138 m_renderer->DrawButtonBorder(m_dc, m_rect, flags, &m_rect);
139
193e19cf
RR
140 // Why do this here?
141 // m_renderer->DrawButtonSurface(m_dc, wxTHEME_BG_COLOUR(m_window), m_rect, flags );
1e6feb95
VZ
142}
143
144void 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 */
153void 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.Ok() )
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 }
9a6384ca 181#if wxUSE_IMAGE
1e6feb95
VZ
182 else if ( stretch & wxEXPAND )
183 {
184 // stretch bitmap to fill the entire control
387ebd3e 185 bmp = wxBitmap(wxImage(bmp.ConvertToImage()).Scale(rect.width, rect.height));
1e6feb95 186 }
9a6384ca 187#endif // wxUSE_IMAGE
1e6feb95
VZ
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
a290fa5a 218 dc.DrawBitmap(bmp, x, y, true /* use mask */);
1e6feb95
VZ
219}
220
9a6384ca
WS
221#if wxUSE_SCROLLBAR
222
1e6feb95 223void wxControlRenderer::DrawScrollbar(const wxScrollBar *scrollbar,
61fef19b 224 int WXUNUSED(thumbPosOld))
1e6feb95
VZ
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();
9a83f860
VZ
231 wxLogTrace(wxT("scrollbar"),
232 wxT("%s redraw: update box is (%d, %d)-(%d, %d)"),
233 scrollbar->IsVertical() ? wxT("vert") : wxT("horz"),
1e6feb95
VZ
234 rectUpdate.GetLeft(),
235 rectUpdate.GetTop(),
236 rectUpdate.GetRight(),
237 rectUpdate.GetBottom());
238
239#if 0 //def WXDEBUG_SCROLLBAR
a290fa5a 240 static bool s_refreshDebug = false;
1e6feb95
VZ
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
56195504 266 wxRect rectBar = scrollbar->GetScrollbarRect(elem);
1e6feb95
VZ
267
268 if ( rgnUpdate.Contains(rectBar) )
269 {
9a83f860
VZ
270 wxLogTrace(wxT("scrollbar"),
271 wxT("drawing bar part %d at (%d, %d)-(%d, %d)"),
1e6feb95
VZ
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
56195504 291 wxRect rectArrow = scrollbar->GetScrollbarRect(elem);
1e6feb95
VZ
292 if ( rgnUpdate.Contains(rectArrow) )
293 {
9a83f860
VZ
294 wxLogTrace(wxT("scrollbar"),
295 wxT("drawing arrow %d at (%d, %d)-(%d, %d)"),
1e6feb95
VZ
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,
a290fa5a 307 true // draw a scrollbar arrow, not just an arrow
1e6feb95
VZ
308 );
309 }
310 }
311
312 // TODO: support for page arrows
313
314 // and the thumb
315 wxScrollBar::Element elem = wxScrollBar::Element_Thumb;
56195504 316 wxRect rectThumb = scrollbar->GetScrollbarRect(elem);
1e6feb95
VZ
317 if ( rectThumb.width && rectThumb.height && rgnUpdate.Contains(rectThumb) )
318 {
9a83f860
VZ
319 wxLogTrace(wxT("scrollbar"),
320 wxT("drawing thumb at (%d, %d)-(%d, %d)"),
1e6feb95
VZ
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
9a6384ca
WS
333#endif // wxUSE_SCROLLBAR
334
1e6feb95
VZ
335void wxControlRenderer::DrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
336{
337 wxASSERT_MSG( x1 == x2 || y1 == y2,
9a83f860 338 wxT("line must be either horizontal or vertical") );
1e6feb95
VZ
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
348void wxControlRenderer::DrawItems(const wxListBox *lbox,
349 size_t itemFirst, size_t itemLast)
350{
351 DoDrawItems(lbox, itemFirst, itemLast);
352}
353
354void wxControlRenderer::DoDrawItems(const wxListBox *lbox,
355 size_t itemFirst, size_t itemLast,
8a39593e 356#if wxUSE_CHECKLISTBOX
147b8a4a 357 bool isCheckLbox
8a39593e 358#else
147b8a4a 359 bool WXUNUSED(isCheckLbox)
8a39593e 360#endif
147b8a4a 361 )
1e6feb95
VZ
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
429void wxControlRenderer::DrawCheckItems(const wxCheckListBox *lbox,
430 size_t itemFirst, size_t itemLast)
431{
a290fa5a 432 DoDrawItems(lbox, itemFirst, itemLast, true);
1e6feb95
VZ
433}
434
435#endif // wxUSE_CHECKLISTBOX
436
437#if wxUSE_GAUGE
438
439void 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
a407ff6a
VZ
460 wxColour col = m_window->UseFgCol() ? m_window->GetForegroundColour()
461 : wxTHEME_COLOUR(GAUGE);
462 m_dc.SetBrush(wxBrush(col, wxSOLID));
1e6feb95
VZ
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!
9a83f860 488 wxCHECK_RET( step, wxT("invalid wxGauge step") );
1e6feb95
VZ
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
32b13913
WS
495 wxCoord *px;
496 wxCoord dx, dy;
1e6feb95
VZ
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