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