1/////////////////////////////////////////////////////////////////////////////// 2// Name: src/generic/renderg.cpp 3// Purpose: generic implementation of wxRendererNative (for any platform) 4// Author: Vadim Zeitlin 5// Modified by: 6// Created: 20.07.2003 7// RCS-ID: $Id: renderg.cpp 45498 2007-04-16 13:03:05Z VZ $ 8// Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org> 9// License: wxWindows license 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#include "wx/renderer.h" 28 29#ifndef WX_PRECOMP 30 #include "wx/string.h" 31 #include "wx/dc.h" 32 #include "wx/settings.h" 33 #include "wx/gdicmn.h" 34 #include "wx/module.h" 35#endif //WX_PRECOMP 36 37#include "wx/splitter.h" 38#include "wx/dcmirror.h" 39 40// ---------------------------------------------------------------------------- 41// wxRendererGeneric: our wxRendererNative implementation 42// ---------------------------------------------------------------------------- 43 44class WXDLLEXPORT wxRendererGeneric : public wxRendererNative 45{ 46public: 47 wxRendererGeneric(); 48 49 virtual int DrawHeaderButton(wxWindow *win, 50 wxDC& dc, 51 const wxRect& rect, 52 int flags = 0, 53 wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE, 54 wxHeaderButtonParams* params = NULL); 55 56 virtual int DrawHeaderButtonContents(wxWindow *win, 57 wxDC& dc, 58 const wxRect& rect, 59 int flags = 0, 60 wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE, 61 wxHeaderButtonParams* params = NULL); 62 63 virtual int GetHeaderButtonHeight(wxWindow *win); 64 65 virtual void DrawTreeItemButton(wxWindow *win, 66 wxDC& dc, 67 const wxRect& rect, 68 int flags = 0); 69 70 virtual void DrawSplitterBorder(wxWindow *win, 71 wxDC& dc, 72 const wxRect& rect, 73 int flags = 0); 74 75 virtual void DrawSplitterSash(wxWindow *win, 76 wxDC& dc, 77 const wxSize& size, 78 wxCoord position, 79 wxOrientation orient, 80 int flags = 0); 81 82 virtual void DrawComboBoxDropButton(wxWindow *win, 83 wxDC& dc, 84 const wxRect& rect, 85 int flags = 0); 86 87 virtual void DrawDropArrow(wxWindow *win, 88 wxDC& dc, 89 const wxRect& rect, 90 int flags = 0); 91 92 virtual void DrawCheckBox(wxWindow *win, 93 wxDC& dc, 94 const wxRect& rect, 95 int flags = 0); 96 97 virtual void DrawPushButton(wxWindow *win, 98 wxDC& dc, 99 const wxRect& rect, 100 int flags = 0); 101 102 virtual void DrawItemSelectionRect(wxWindow *win, 103 wxDC& dc, 104 const wxRect& rect, 105 int flags = 0); 106 107 virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win); 108 109 virtual wxRendererVersion GetVersion() const 110 { 111 return wxRendererVersion(wxRendererVersion::Current_Version, 112 wxRendererVersion::Current_Age); 113 } 114 115 116 // Cleanup by deleting standard renderer 117 static void Cleanup(); 118 119 // Get the generic object 120 static wxRendererGeneric* DoGetGeneric(); 121 122protected: 123 // draw the rectange using the first pen for the left and top sides and 124 // the second one for the bottom and right ones 125 void DrawShadedRect(wxDC& dc, wxRect *rect, 126 const wxPen& pen1, const wxPen& pen2); 127 128 // the standard pens 129 wxPen m_penBlack, 130 m_penDarkGrey, 131 m_penLightGrey, 132 m_penHighlight; 133 134 static wxRendererGeneric* sm_rendererGeneric; 135}; 136 137// ============================================================================ 138// wxRendererGeneric implementation 139// ============================================================================ 140 141// Get the generic object 142wxRendererGeneric* wxRendererGeneric::DoGetGeneric() 143{ 144 if (!sm_rendererGeneric) 145 sm_rendererGeneric = new wxRendererGeneric; 146 return sm_rendererGeneric; 147} 148 149// ---------------------------------------------------------------------------- 150// wxRendererGeneric creation 151// ---------------------------------------------------------------------------- 152 153/* static */ 154wxRendererNative& wxRendererNative::GetGeneric() 155{ 156 return * wxRendererGeneric::DoGetGeneric(); 157} 158 159void wxRendererGeneric::Cleanup() 160{ 161 if (sm_rendererGeneric) 162 delete sm_rendererGeneric; 163 164 sm_rendererGeneric = NULL; 165} 166 167wxRendererGeneric* wxRendererGeneric::sm_rendererGeneric = NULL; 168 169wxRendererGeneric::wxRendererGeneric() 170 : m_penBlack(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW)), 171 m_penDarkGrey(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW)), 172 m_penLightGrey(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE)), 173 m_penHighlight(wxSystemSettings::GetColour(wxSYS_COLOUR_3DHIGHLIGHT)) 174{ 175} 176 177// ---------------------------------------------------------------------------- 178// wxRendererGeneric helpers 179// ---------------------------------------------------------------------------- 180 181void 182wxRendererGeneric::DrawShadedRect(wxDC& dc, 183 wxRect *rect, 184 const wxPen& pen1, 185 const wxPen& pen2) 186{ 187 // draw the rectangle 188 dc.SetPen(pen1); 189 dc.DrawLine(rect->GetLeft(), rect->GetTop(), 190 rect->GetLeft(), rect->GetBottom()); 191 dc.DrawLine(rect->GetLeft() + 1, rect->GetTop(), 192 rect->GetRight(), rect->GetTop()); 193 dc.SetPen(pen2); 194 dc.DrawLine(rect->GetRight(), rect->GetTop(), 195 rect->GetRight(), rect->GetBottom()); 196 dc.DrawLine(rect->GetLeft(), rect->GetBottom(), 197 rect->GetRight() + 1, rect->GetBottom()); 198 199 // adjust the rect 200 rect->Inflate(-1); 201} 202 203// ---------------------------------------------------------------------------- 204// tree/list ctrl drawing 205// ---------------------------------------------------------------------------- 206 207int 208wxRendererGeneric::DrawHeaderButton(wxWindow* win, 209 wxDC& dc, 210 const wxRect& rect, 211 int flags, 212 wxHeaderSortIconType sortArrow, 213 wxHeaderButtonParams* params) 214{ 215 const int CORNER = 1; 216 217 const wxCoord x = rect.x, 218 y = rect.y, 219 w = rect.width, 220 h = rect.height; 221 222 dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE))); 223 dc.SetPen(*wxTRANSPARENT_PEN); 224 dc.DrawRectangle(rect); 225 226 dc.SetBrush(*wxTRANSPARENT_BRUSH); 227 228 dc.SetPen(m_penBlack); 229 dc.DrawLine( x+w-CORNER+1, y, x+w, y+h ); // right (outer) 230 dc.DrawRectangle( x, y+h, w+1, 1 ); // bottom (outer) 231 232 dc.SetPen(m_penDarkGrey); 233 dc.DrawLine( x+w-CORNER, y, x+w-1, y+h ); // right (inner) 234 dc.DrawRectangle( x+1, y+h-1, w-2, 1 ); // bottom (inner) 235 236 dc.SetPen(m_penHighlight); 237 dc.DrawRectangle( x, y, w-CORNER+1, 1 ); // top (outer) 238 dc.DrawRectangle( x, y, 1, h ); // left (outer) 239 dc.DrawLine( x, y+h-1, x+1, y+h-1 ); 240 dc.DrawLine( x+w-1, y, x+w-1, y+1 ); 241 242 return DrawHeaderButtonContents(win, dc, rect, flags, sortArrow, params); 243} 244 245 246int 247wxRendererGeneric::DrawHeaderButtonContents(wxWindow *win, 248 wxDC& dc, 249 const wxRect& rect, 250 int flags, 251 wxHeaderSortIconType sortArrow, 252 wxHeaderButtonParams* params) 253{ 254 int labelWidth = 0; 255 256 // Mark this item as selected. For the generic version we'll just draw an 257 // underline 258 if ( flags & wxCONTROL_SELECTED ) 259 { 260 // draw a line at the bottom of the header button, overlaying the 261 // native hot-tracking line (on XP) 262 const int penwidth = 3; 263 int y = rect.y + rect.height + 1 - penwidth; 264 wxColour c = (params && params->m_selectionColour.Ok()) ? 265 params->m_selectionColour : wxColour(0x66, 0x66, 0x66); 266 wxPen pen(c, penwidth); 267 pen.SetCap(wxCAP_BUTT); 268 dc.SetPen(pen); 269 dc.DrawLine(rect.x, y, rect.x + rect.width, y); 270 } 271 272 // Draw an up or down arrow 273 int arrowSpace = 0; 274 if (sortArrow != wxHDR_SORT_ICON_NONE ) 275 { 276 wxRect ar = rect; 277 278 // make a rect for the arrow 279 ar.height = 4; 280 ar.width = 8; 281 ar.y += (rect.height - ar.height)/2; 282 ar.x = ar.x + rect.width - 3*ar.width/2; 283 arrowSpace = 3*ar.width/2; // space to preserve when drawing the label 284 285 wxPoint triPt[3]; 286 if ( sortArrow & wxHDR_SORT_ICON_UP ) 287 { 288 triPt[0].x = ar.width / 2; 289 triPt[0].y = 0; 290 triPt[1].x = ar.width; 291 triPt[1].y = ar.height; 292 triPt[2].x = 0; 293 triPt[2].y = ar.height; 294 } 295 else 296 { 297 triPt[0].x = 0; 298 triPt[0].y = 0; 299 triPt[1].x = ar.width; 300 triPt[1].y = 0; 301 triPt[2].x = ar.width / 2; 302 triPt[2].y = ar.height; 303 } 304 305 wxColour c = (params && params->m_arrowColour.Ok()) ? 306 params->m_arrowColour : wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW); 307 dc.SetPen(wxPen(c)); 308 dc.SetBrush(wxBrush(c)); 309 dc.DrawPolygon( 3, triPt, ar.x, ar.y); 310 } 311 labelWidth += arrowSpace; 312 313 const int margin = 5; // number of pixels to reserve on either side of the label 314 int bmpWidth = 0; 315 int txtEnd = 0; 316 317 if ( params && params->m_labelBitmap.Ok() ) 318 bmpWidth = params->m_labelBitmap.GetWidth() + 2; 319 320 labelWidth += bmpWidth + 2*margin; 321 322 // Draw a label if one is given 323 if ( params && !params->m_labelText.empty() ) 324 { 325 wxFont font = params->m_labelFont.Ok() ? 326 params->m_labelFont : win->GetFont(); 327 wxColour clr = params->m_labelColour.Ok() ? 328 params->m_labelColour : win->GetForegroundColour(); 329 330 wxString label( params->m_labelText ); 331 332 dc.SetFont(font); 333 dc.SetTextForeground(clr); 334 dc.SetBackgroundMode(wxTRANSPARENT); 335 336 int tw, th, td, x, y; 337 dc.GetTextExtent( label, &tw, &th, &td); 338 labelWidth += tw; 339 y = rect.y + wxMax(0, (rect.height - (th+td)) / 2); 340 341 // truncate and add an ellipsis (...) if the text is too wide. 342 int targetWidth = rect.width - arrowSpace - bmpWidth - 2*margin; 343 if ( tw > targetWidth ) 344 { 345 int ellipsisWidth; 346 dc.GetTextExtent( wxT("..."), &ellipsisWidth, NULL); 347 do { 348 label.Truncate( label.length() - 1 ); 349 dc.GetTextExtent( label, &tw, &th); 350 } while (tw + ellipsisWidth > targetWidth && label.length() ); 351 label.append( wxT("...") ); 352 tw += ellipsisWidth; 353 } 354 355 switch (params->m_labelAlignment) 356 { 357 default: 358 case wxALIGN_LEFT: 359 x = rect.x + margin; 360 break; 361 case wxALIGN_CENTER: 362 x = rect.x + wxMax(0, (rect.width - arrowSpace - tw - bmpWidth)/2); 363 break; 364 case wxALIGN_RIGHT: 365 x = rect.x + wxMax(0, rect.width - arrowSpace - margin - tw - bmpWidth); 366 break; 367 } 368 369 dc.DrawText(label, x, y); 370 txtEnd = x + tw + 2; 371 } 372 373 // draw the bitmap if there is one 374 if ( params && params->m_labelBitmap.Ok() ) 375 { 376 int w, h, x, y; 377 w = params->m_labelBitmap.GetWidth(); 378 h = params->m_labelBitmap.GetHeight(); 379 380 y = rect.y + wxMax(1, (rect.height - h) / 2); 381 382 // if there is a text label, then put the bitmap at the end of the label 383 if ( txtEnd != 0 ) 384 { 385 x = txtEnd; 386 } 387 // otherwise use the alignment flags 388 else 389 { 390 switch (params->m_labelAlignment) 391 { 392 default: 393 case wxALIGN_LEFT: 394 x = rect.x + margin; 395 break; 396 case wxALIGN_CENTER: 397 x = rect.x + wxMax(1, (rect.width - arrowSpace - w)/2); 398 break; 399 case wxALIGN_RIGHT: 400 x = rect.x + wxMax(1, rect.width - arrowSpace - margin - w); 401 break; 402 } 403 } 404 dc.DrawBitmap(params->m_labelBitmap, x, y, true); 405 } 406 return labelWidth; 407} 408 409 410int wxRendererGeneric::GetHeaderButtonHeight(wxWindow *win) 411{ 412 // Copied and adapted from src/generic/listctrl.cpp 413 const int HEADER_OFFSET_Y = 1; 414 const int EXTRA_HEIGHT = 4; 415 416 int w=0, h=14, d=0; 417 if (win) 418 win->GetTextExtent(wxT("Hg"), &w, &h, &d); 419 420 return h + d + 2 * HEADER_OFFSET_Y + EXTRA_HEIGHT; 421} 422 423 424// draw the plus or minus sign 425void 426wxRendererGeneric::DrawTreeItemButton(wxWindow * WXUNUSED(win), 427 wxDC& dc, 428 const wxRect& rect, 429 int flags) 430{ 431 // store settings 432 wxDCPenChanger penChanger(dc, *wxGREY_PEN); 433 wxDCBrushChanger brushChanger(dc, *wxWHITE_BRUSH); 434 435 dc.DrawRectangle(rect); 436 437 // black lines 438 const wxCoord xMiddle = rect.x + rect.width/2; 439 const wxCoord yMiddle = rect.y + rect.height/2; 440 441 // half of the length of the horz lines in "-" and "+" 442 const wxCoord halfWidth = rect.width/2 - 2; 443 dc.SetPen(*wxBLACK_PEN); 444 dc.DrawLine(xMiddle - halfWidth, yMiddle, 445 xMiddle + halfWidth + 1, yMiddle); 446 447 if ( !(flags & wxCONTROL_EXPANDED) ) 448 { 449 // turn "-" into "+" 450 const wxCoord halfHeight = rect.height/2 - 2; 451 dc.DrawLine(xMiddle, yMiddle - halfHeight, 452 xMiddle, yMiddle + halfHeight + 1); 453 } 454} 455 456// ---------------------------------------------------------------------------- 457// sash drawing 458// ---------------------------------------------------------------------------- 459 460wxSplitterRenderParams 461wxRendererGeneric::GetSplitterParams(const wxWindow *win) 462{ 463 // see below 464 wxCoord sashWidth, 465 border; 466 467 if ( win->HasFlag(wxSP_3DSASH) ) 468 sashWidth = 7; 469 else if ( win->HasFlag(wxSP_NOSASH) ) 470 sashWidth = 0; 471 else // no 3D effect 472 sashWidth = 3; 473 474 if ( win->HasFlag(wxSP_3DBORDER) ) 475 border = 2; 476 else // no 3D effect 477 border = 0; 478 479 return wxSplitterRenderParams(sashWidth, border, false); 480} 481 482void 483wxRendererGeneric::DrawSplitterBorder(wxWindow *win, 484 wxDC& dc, 485 const wxRect& rectOrig, 486 int WXUNUSED(falgs)) 487{ 488 if ( win->HasFlag(wxSP_3DBORDER) ) 489 { 490 wxRect rect = rectOrig; 491 DrawShadedRect(dc, &rect, m_penDarkGrey, m_penHighlight); 492 DrawShadedRect(dc, &rect, m_penBlack, m_penLightGrey); 493 } 494} 495 496void 497wxRendererGeneric::DrawSplitterSash(wxWindow *win, 498 wxDC& dcReal, 499 const wxSize& sizeReal, 500 wxCoord position, 501 wxOrientation orient, 502 int WXUNUSED(flags)) 503{ 504 // to avoid duplicating the same code for horizontal and vertical sashes, 505 // simply mirror the DC instead if needed (i.e. if horz splitter) 506 wxMirrorDC dc(dcReal, orient != wxVERTICAL); 507 wxSize size = dc.Reflect(sizeReal); 508 509 510 // we draw a Win32-like grey sash with possible 3D border here: 511 // 512 // ---- this is position 513 // / 514 // v 515 // dWGGGDd 516 // GWGGGDB 517 // GWGGGDB where G is light grey (face) 518 // GWGGGDB W white (light) 519 // GWGGGDB D dark grey (shadow) 520 // GWGGGDB B black (dark shadow) 521 // GWGGGDB 522 // GWGGGDB and lower letters are our border (already drawn) 523 // GWGGGDB 524 // wWGGGDd 525 // 526 // only the middle 3 columns are drawn unless wxSP_3D is specified 527 528 const wxCoord h = size.y; 529 wxCoord offset = 0; 530 531 // If we're drawing the border, draw the sash 3d lines shorter 532 if ( win->HasFlag(wxSP_3DBORDER) ) 533 { 534 offset = 1; 535 } 536 537 dc.SetPen(*wxTRANSPARENT_PEN); 538 dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE))); 539 540 if ( win->HasFlag(wxSP_3DSASH) ) 541 { 542 // Draw the 3D sash 543 dc.DrawRectangle(position + 2, 0, 3, h); 544 545 dc.SetPen(m_penLightGrey); 546 dc.DrawLine(position, offset, position, h - offset); 547 548 dc.SetPen(m_penHighlight); 549 dc.DrawLine(position + 1, 0, position + 1, h); 550 551 dc.SetPen(m_penDarkGrey); 552 dc.DrawLine(position + 5, 0, position + 5, h); 553 554 dc.SetPen(m_penBlack); 555 dc.DrawLine(position + 6, offset, position + 6, h - offset); 556 } 557 else 558 { 559 // Draw a flat sash 560 dc.DrawRectangle(position, 0, 3, h); 561 } 562} 563 564// ---------------------------------------------------------------------------- 565// button drawing 566// ---------------------------------------------------------------------------- 567 568void 569wxRendererGeneric::DrawComboBoxDropButton(wxWindow *win, 570 wxDC& dc, 571 const wxRect& rect, 572 int flags) 573{ 574 DrawPushButton(win,dc,rect,flags); 575 DrawDropArrow(win,dc,rect,flags); 576} 577 578void 579wxRendererGeneric::DrawDropArrow(wxWindow *win, 580 wxDC& dc, 581 const wxRect& rect, 582 int WXUNUSED(flags)) 583{ 584 // This generic implementation should be good 585 // enough for Windows platforms (including XP). 586 587 int arrowHalf = rect.width/5; 588 int rectMid = rect.width / 2; 589 int arrowTopY = (rect.height/2) - (arrowHalf/2); 590 591 // This should always result in arrow with odd width. 592 wxPoint pt[] = 593 { 594 wxPoint(rectMid - arrowHalf, arrowTopY), 595 wxPoint(rectMid + arrowHalf, arrowTopY), 596 wxPoint(rectMid, arrowTopY + arrowHalf) 597 }; 598 dc.SetBrush(wxBrush(win->GetForegroundColour())); 599 dc.SetPen(wxPen(win->GetForegroundColour())); 600 dc.DrawPolygon(WXSIZEOF(pt), pt, rect.x, rect.y); 601} 602 603void 604wxRendererGeneric::DrawCheckBox(wxWindow *WXUNUSED(win), 605 wxDC& dc, 606 const wxRect& rect, 607 int flags) 608{ 609 dc.SetPen(*(flags & wxCONTROL_DISABLED ? wxGREY_PEN : wxBLACK_PEN)); 610 dc.SetBrush( *wxTRANSPARENT_BRUSH ); 611 dc.DrawRectangle(rect); 612 613 if ( flags & wxCONTROL_CHECKED ) 614 { 615 dc.DrawCheckMark(rect.Deflate(2, 2)); 616 } 617} 618 619void 620wxRendererGeneric::DrawPushButton(wxWindow *win, 621 wxDC& dc, 622 const wxRect& rect, 623 int flags) 624{ 625 // Don't try anything too fancy. It'll just turn out looking 626 // out-of-place on most platforms. 627 wxColour bgCol = flags & wxCONTROL_DISABLED ? 628 wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE) : 629 win->GetBackgroundColour(); 630 dc.SetBrush(wxBrush(bgCol)); 631 dc.SetPen(wxPen(bgCol)); 632 dc.DrawRectangle(rect); 633} 634 635void 636wxRendererGeneric::DrawItemSelectionRect(wxWindow * WXUNUSED(win), 637 wxDC& dc, 638 const wxRect& rect, 639 int flags) 640{ 641 wxBrush brush; 642 if ( flags & wxCONTROL_SELECTED ) 643 { 644 if ( flags & wxCONTROL_FOCUSED ) 645 { 646 brush = wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)); 647 } 648 else // !focused 649 { 650 brush = wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW)); 651 } 652 } 653 else // !selected 654 { 655 brush = *wxTRANSPARENT_BRUSH; 656 } 657 658 dc.SetBrush(brush); 659 dc.SetPen(flags & wxCONTROL_CURRENT ? *wxBLACK_PEN : *wxTRANSPARENT_PEN); 660 661 dc.DrawRectangle( rect ); 662} 663 664 665// ---------------------------------------------------------------------------- 666// A module to allow cleanup of generic renderer. 667// ---------------------------------------------------------------------------- 668 669class wxGenericRendererModule: public wxModule 670{ 671DECLARE_DYNAMIC_CLASS(wxGenericRendererModule) 672public: 673 wxGenericRendererModule() {} 674 bool OnInit() { return true; } 675 void OnExit() { wxRendererGeneric::Cleanup(); } 676}; 677 678IMPLEMENT_DYNAMIC_CLASS(wxGenericRendererModule, wxModule) 679