1/////////////////////////////////////////////////////////////////////////////// 2// Name: src/univ/window.cpp 3// Purpose: implementation of extra wxWindow methods for wxUniv port 4// Author: Vadim Zeitlin 5// Modified by: 6// Created: 06.08.00 7// RCS-ID: $Id: winuniv.cpp 46437 2007-06-13 04:35:23Z SC $ 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#include "wx/window.h" 28 29#ifndef WX_PRECOMP 30 #include "wx/app.h" 31 #include "wx/dcclient.h" 32 #include "wx/dcmemory.h" 33 #include "wx/event.h" 34 #include "wx/scrolbar.h" 35 #include "wx/menu.h" 36 #include "wx/frame.h" 37 #include "wx/log.h" 38#endif // WX_PRECOMP 39 40#include "wx/univ/colschem.h" 41#include "wx/univ/renderer.h" 42#include "wx/univ/theme.h" 43 44#if wxUSE_CARET 45 #include "wx/caret.h" 46#endif // wxUSE_CARET 47 48// turn Refresh() debugging on/off 49#define WXDEBUG_REFRESH 50 51#ifndef __WXDEBUG__ 52 #undef WXDEBUG_REFRESH 53#endif 54 55#if defined(WXDEBUG_REFRESH) && defined(__WXMSW__) && !defined(__WXMICROWIN__) 56#include "wx/msw/private.h" 57#endif 58 59// ============================================================================ 60// implementation 61// ============================================================================ 62 63// ---------------------------------------------------------------------------- 64// event tables 65// ---------------------------------------------------------------------------- 66 67// we can't use wxWindowNative here as it won't be expanded inside the macro 68#if defined(__WXMSW__) 69 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowMSW) 70#elif defined(__WXGTK__) 71 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowGTK) 72#elif defined(__WXMGL__) 73 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowMGL) 74#elif defined(__WXDFB__) 75 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowDFB) 76#elif defined(__WXX11__) 77 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowX11) 78#elif defined(__WXPM__) 79 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowOS2) 80#elif defined(__WXMAC__) 81 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowMac) 82#endif 83 84BEGIN_EVENT_TABLE(wxWindow, wxWindowNative) 85 EVT_SIZE(wxWindow::OnSize) 86 87#if wxUSE_ACCEL || wxUSE_MENUS 88 EVT_KEY_DOWN(wxWindow::OnKeyDown) 89#endif // wxUSE_ACCEL 90 91#if wxUSE_MENUS 92 EVT_CHAR(wxWindow::OnChar) 93 EVT_KEY_UP(wxWindow::OnKeyUp) 94#endif // wxUSE_MENUS 95 96 EVT_PAINT(wxWindow::OnPaint) 97 EVT_NC_PAINT(wxWindow::OnNcPaint) 98 EVT_ERASE_BACKGROUND(wxWindow::OnErase) 99END_EVENT_TABLE() 100 101// ---------------------------------------------------------------------------- 102// creation 103// ---------------------------------------------------------------------------- 104 105void wxWindow::Init() 106{ 107#if wxUSE_SCROLLBAR 108 m_scrollbarVert = 109 m_scrollbarHorz = (wxScrollBar *)NULL; 110#endif // wxUSE_SCROLLBAR 111 112 m_isCurrent = false; 113 114 m_renderer = wxTheme::Get()->GetRenderer(); 115 116 m_oldSize.x = wxDefaultCoord; 117 m_oldSize.y = wxDefaultCoord; 118} 119 120bool wxWindow::Create(wxWindow *parent, 121 wxWindowID id, 122 const wxPoint& pos, 123 const wxSize& size, 124 long style, 125 const wxString& name) 126{ 127 long actualStyle = style; 128 129 // we add wxCLIP_CHILDREN to get the same ("natural") behaviour under MSW 130 // as under the other platforms 131 actualStyle |= wxCLIP_CHILDREN; 132 133 actualStyle &= ~wxVSCROLL; 134 actualStyle &= ~wxHSCROLL; 135 136#ifdef __WXMSW__ 137 // without this, borders (non-client areas in general) are not repainted 138 // correctly when resizing; apparently, native NC areas are fully repainted 139 // even without this style by MSW, but wxUniv implements client area 140 // itself, so it doesn't work correctly for us 141 // 142 // FIXME: this is very expensive, we need to fix the (commented-out) code 143 // in OnSize() instead 144 actualStyle |= wxFULL_REPAINT_ON_RESIZE; 145#endif 146 147 if ( !wxWindowNative::Create(parent, id, pos, size, actualStyle, name) ) 148 return false; 149 150 // Set full style again, including those we didn't want present 151 // when calling the base window Create(). 152 wxWindowBase::SetWindowStyleFlag(style); 153 154 // if we allow or should always have a vertical scrollbar, make it 155 if ( style & wxVSCROLL || style & wxALWAYS_SHOW_SB ) 156 { 157#if wxUSE_TWO_WINDOWS 158 SetInsertIntoMain( true ); 159#endif 160#if wxUSE_SCROLLBAR 161 m_scrollbarVert = new wxScrollBar(this, wxID_ANY, 162 wxDefaultPosition, wxDefaultSize, 163 wxSB_VERTICAL); 164#endif // wxUSE_SCROLLBAR 165#if wxUSE_TWO_WINDOWS 166 SetInsertIntoMain( false ); 167#endif 168 } 169 170 // if we should allow a horizontal scrollbar, make it 171 if ( style & wxHSCROLL ) 172 { 173#if wxUSE_TWO_WINDOWS 174 SetInsertIntoMain( true ); 175#endif 176#if wxUSE_SCROLLBAR 177 m_scrollbarHorz = new wxScrollBar(this, wxID_ANY, 178 wxDefaultPosition, wxDefaultSize, 179 wxSB_HORIZONTAL); 180#endif // wxUSE_SCROLLBAR 181#if wxUSE_TWO_WINDOWS 182 SetInsertIntoMain( false ); 183#endif 184 } 185 186#if wxUSE_SCROLLBAR 187 if (m_scrollbarHorz || m_scrollbarVert) 188 { 189 // position it/them 190 PositionScrollbars(); 191 } 192#endif // wxUSE_SCROLLBAR 193 194 return true; 195} 196 197wxWindow::~wxWindow() 198{ 199 m_isBeingDeleted = true; 200 201#if wxUSE_SCROLLBAR 202 // clear pointers to scrollbar before deleting the children: they are 203 // children and so will be deleted by DestroyChildren() call below and if 204 // any code using the scrollbars would be called in the process or from 205 // ~wxWindowBase, the app would crash: 206 m_scrollbarVert = m_scrollbarHorz = NULL; 207#endif 208 209 // we have to destroy our children before we're destroyed because our 210 // children suppose that we're of type wxWindow, not just wxWindowNative, 211 // and so bad things may happen if they're deleted from the base class dtor 212 // as by then we're not a wxWindow any longer and wxUniv-specific virtual 213 // functions can't be called 214 DestroyChildren(); 215} 216 217// ---------------------------------------------------------------------------- 218// background pixmap 219// ---------------------------------------------------------------------------- 220 221void wxWindow::SetBackground(const wxBitmap& bitmap, 222 int alignment, 223 wxStretch stretch) 224{ 225 m_bitmapBg = bitmap; 226 m_alignBgBitmap = alignment; 227 m_stretchBgBitmap = stretch; 228} 229 230const wxBitmap& wxWindow::GetBackgroundBitmap(int *alignment, 231 wxStretch *stretch) const 232{ 233 if ( m_bitmapBg.Ok() ) 234 { 235 if ( alignment ) 236 *alignment = m_alignBgBitmap; 237 if ( stretch ) 238 *stretch = m_stretchBgBitmap; 239 } 240 241 return m_bitmapBg; 242} 243 244// ---------------------------------------------------------------------------- 245// painting 246// ---------------------------------------------------------------------------- 247 248// the event handlers executed when the window must be repainted 249void wxWindow::OnNcPaint(wxNcPaintEvent& WXUNUSED(event)) 250{ 251 if ( m_renderer ) 252 { 253 // get the window rect 254 wxRect rect(GetSize()); 255 256#if wxUSE_SCROLLBAR 257 // if the scrollbars are outside the border, we must adjust the rect to 258 // exclude them 259 if ( !m_renderer->AreScrollbarsInsideBorder() ) 260 { 261 wxScrollBar *scrollbar = GetScrollbar(wxVERTICAL); 262 if ( scrollbar ) 263 rect.width -= scrollbar->GetSize().x; 264 265 scrollbar = GetScrollbar(wxHORIZONTAL); 266 if ( scrollbar ) 267 rect.height -= scrollbar->GetSize().y; 268 } 269#endif // wxUSE_SCROLLBAR 270 271 // get the DC and draw the border on it 272 wxWindowDC dc(this); 273 DoDrawBorder(dc, rect); 274 } 275} 276 277void wxWindow::OnPaint(wxPaintEvent& event) 278{ 279 if ( !m_renderer ) 280 { 281 // it is a native control which paints itself 282 event.Skip(); 283 } 284 else 285 { 286 // get the DC to use and create renderer on it 287 wxPaintDC dc(this); 288 wxControlRenderer renderer(this, dc, m_renderer); 289 290 // draw the control 291 DoDraw(&renderer); 292 } 293} 294 295// the event handler executed when the window background must be painted 296void wxWindow::OnErase(wxEraseEvent& event) 297{ 298 if ( !m_renderer ) 299 { 300 event.Skip(); 301 302 return; 303 } 304 305 DoDrawBackground(*event.GetDC()); 306 307#if wxUSE_SCROLLBAR 308 // if we have both scrollbars, we also have a square in the corner between 309 // them which we must paint 310 if ( m_scrollbarVert && m_scrollbarHorz ) 311 { 312 wxSize size = GetSize(); 313 wxRect rectClient = GetClientRect(), 314 rectBorder = m_renderer->GetBorderDimensions(GetBorder()); 315 316 wxRect rectCorner; 317 rectCorner.x = rectClient.GetRight() + 1; 318 rectCorner.y = rectClient.GetBottom() + 1; 319 rectCorner.SetRight(size.x - rectBorder.width); 320 rectCorner.SetBottom(size.y - rectBorder.height); 321 322 if ( GetUpdateRegion().Contains(rectCorner) ) 323 { 324 m_renderer->DrawScrollCorner(*event.GetDC(), rectCorner); 325 } 326 } 327#endif // wxUSE_SCROLLBAR 328} 329 330bool wxWindow::DoDrawBackground(wxDC& dc) 331{ 332 wxRect rect; 333 334 wxSize size = GetSize(); // Why not GetClientSize() ? 335 rect.x = 0; 336 rect.y = 0; 337 rect.width = size.x; 338 rect.height = size.y; 339 340 wxWindow * const parent = GetParent(); 341 if ( HasTransparentBackground() && !UseBgCol() && parent ) 342 { 343 wxASSERT( !IsTopLevel() ); 344 345 wxPoint pos = GetPosition(); 346 347 AdjustForParentClientOrigin( pos.x, pos.y, 0 ); 348 349 // Adjust DC logical origin 350 wxCoord org_x, org_y, x, y; 351 dc.GetLogicalOrigin( &org_x, &org_y ); 352 x = org_x + pos.x; 353 y = org_y + pos.y; 354 dc.SetLogicalOrigin( x, y ); 355 356 // Adjust draw rect 357 rect.x = pos.x; 358 rect.y = pos.y; 359 360 // Let parent draw the background 361 parent->EraseBackground( dc, rect ); 362 363 // Restore DC logical origin 364 dc.SetLogicalOrigin( org_x, org_y ); 365 } 366 else 367 { 368 // Draw background ourselves 369 EraseBackground( dc, rect ); 370 } 371 372 return true; 373} 374 375void wxWindow::EraseBackground(wxDC& dc, const wxRect& rect) 376{ 377 if ( GetBackgroundBitmap().Ok() ) 378 { 379 // Get the bitmap and the flags 380 int alignment; 381 wxStretch stretch; 382 wxBitmap bmp = GetBackgroundBitmap(&alignment, &stretch); 383 wxControlRenderer::DrawBitmap(dc, bmp, rect, alignment, stretch); 384 } 385 else 386 { 387 // Just fill it with bg colour if no bitmap 388 389 m_renderer->DrawBackground(dc, wxTHEME_BG_COLOUR(this), 390 rect, GetStateFlags()); 391 } 392} 393 394void wxWindow::DoDrawBorder(wxDC& dc, const wxRect& rect) 395{ 396 // draw outline unless the update region is enitrely inside it in which 397 // case we don't need to do it 398#if 0 // doesn't seem to work, why? 399 if ( wxRegion(rect).Contains(GetUpdateRegion().GetBox()) != wxInRegion ) 400#endif 401 { 402 m_renderer->DrawBorder(dc, GetBorder(), rect, GetStateFlags()); 403 } 404} 405 406void wxWindow::DoDraw(wxControlRenderer * WXUNUSED(renderer)) 407{ 408} 409 410void wxWindow::Refresh(bool eraseBackground, const wxRect *rect) 411{ 412 wxRect rectClient; // the same rectangle in client coordinates 413 wxPoint origin = GetClientAreaOrigin(); 414 415 wxSize size = GetClientSize(); 416 417 if ( rect ) 418 { 419 // the rectangle passed as argument is in client coordinates 420 rectClient = *rect; 421 422 // don't refresh anything beyond the client area (scrollbars for 423 // example) 424 if ( rectClient.GetRight() > size.x ) 425 rectClient.SetRight(size.x); 426 if ( rectClient.GetBottom() > size.y ) 427 rectClient.SetBottom(size.y); 428 429 } 430 else // refresh the entire client area 431 { 432 // x,y is already set to 0 by default 433 rectClient.SetSize(size); 434 } 435 436 // convert refresh rectangle to window coordinates: 437 wxRect rectWin(rectClient); 438 rectWin.Offset(origin); 439 440 // debugging helper 441#ifdef WXDEBUG_REFRESH 442 static bool s_refreshDebug = false; 443 if ( s_refreshDebug ) 444 { 445 wxWindowDC dc(this); 446 dc.SetBrush(*wxCYAN_BRUSH); 447 dc.SetPen(*wxTRANSPARENT_PEN); 448 dc.DrawRectangle(rectWin); 449 450 // under Unix we use "--sync" X option for this 451 #if defined(__WXMSW__) && !defined(__WXMICROWIN__) 452 ::GdiFlush(); 453 ::Sleep(200); 454 #endif // __WXMSW__ 455 } 456#endif // WXDEBUG_REFRESH 457 458 wxWindowNative::Refresh(eraseBackground, &rectWin); 459 460 // Refresh all sub controls if any. 461 wxWindowList& children = GetChildren(); 462 for ( wxWindowList::iterator i = children.begin(); i != children.end(); ++i ) 463 { 464 wxWindow *child = *i; 465 // only refresh subcontrols if they are visible: 466 if ( child->IsTopLevel() || !child->IsShown() || child->IsFrozen() ) 467 continue; 468 469 // ...and when the subcontrols are in the update region: 470 wxRect childrect(child->GetRect()); 471 childrect.Intersect(rectClient); 472 if ( childrect.IsEmpty() ) 473 continue; 474 475 // refresh the subcontrol now: 476 childrect.Offset(-child->GetPosition()); 477 // NB: We must call wxWindowNative version because we need to refresh 478 // the entire control, not just its client area, and this is why we 479 // don't account for child client area origin here neither. Also 480 // note that we don't pass eraseBackground to the child, but use 481 // true instead: this is because we can't be sure that 482 // eraseBackground=false is safe for children as well and not only 483 // for the parent. 484 child->wxWindowNative::Refresh(eraseBackground, &childrect); 485 } 486} 487 488// ---------------------------------------------------------------------------- 489// state flags 490// ---------------------------------------------------------------------------- 491 492bool wxWindow::Enable(bool enable) 493{ 494 if ( !wxWindowNative::Enable(enable) ) 495 return false; 496 497 // disabled window can't keep focus 498 if ( FindFocus() == this && GetParent() != NULL ) 499 { 500 GetParent()->SetFocus(); 501 } 502 503 if ( m_renderer ) 504 { 505 // a window with renderer is drawn by ourselves and it has to be 506 // refreshed to reflect its new status 507 Refresh(); 508 } 509 510 return true; 511} 512 513bool wxWindow::IsFocused() const 514{ 515 return FindFocus() == this; 516} 517 518bool wxWindow::IsPressed() const 519{ 520 return false; 521} 522 523bool wxWindow::IsDefault() const 524{ 525 return false; 526} 527 528bool wxWindow::IsCurrent() const 529{ 530 return m_isCurrent; 531} 532 533bool wxWindow::SetCurrent(bool doit) 534{ 535 if ( doit == m_isCurrent ) 536 return false; 537 538 m_isCurrent = doit; 539 540 if ( CanBeHighlighted() ) 541 Refresh(); 542 543 return true; 544} 545 546int wxWindow::GetStateFlags() const 547{ 548 int flags = 0; 549 if ( !IsEnabled() ) 550 flags |= wxCONTROL_DISABLED; 551 552 // the following states are only possible if our application is active - if 553 // it is not, even our default/focused controls shouldn't appear as such 554 if ( wxTheApp->IsActive() ) 555 { 556 if ( IsCurrent() ) 557 flags |= wxCONTROL_CURRENT; 558 if ( IsFocused() ) 559 flags |= wxCONTROL_FOCUSED; 560 if ( IsPressed() ) 561 flags |= wxCONTROL_PRESSED; 562 if ( IsDefault() ) 563 flags |= wxCONTROL_ISDEFAULT; 564 } 565 566 return flags; 567} 568 569// ---------------------------------------------------------------------------- 570// size 571// ---------------------------------------------------------------------------- 572 573void wxWindow::OnSize(wxSizeEvent& event) 574{ 575 event.Skip(); 576 577#if wxUSE_SCROLLBAR 578 if ( m_scrollbarVert || m_scrollbarHorz ) 579 { 580 PositionScrollbars(); 581 } 582#endif // wxUSE_SCROLLBAR 583 584#if 0 // ndef __WXMSW__ 585 // Refresh the area (strip) previously occupied by the border 586 587 if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) && IsShown() ) 588 { 589 // This code assumes that wxSizeEvent.GetSize() returns 590 // the area of the entire window, not just the client 591 // area. 592 wxSize newSize = event.GetSize(); 593 594 if (m_oldSize.x == wxDefaultCoord && m_oldSize.y == wxDefaultCoord) 595 { 596 m_oldSize = newSize; 597 return; 598 } 599 600 if (HasFlag( wxSIMPLE_BORDER )) 601 { 602 if (newSize.y > m_oldSize.y) 603 { 604 wxRect rect; 605 rect.x = 0; 606 rect.width = m_oldSize.x; 607 rect.y = m_oldSize.y-2; 608 rect.height = 1; 609 Refresh( true, &rect ); 610 } 611 else if (newSize.y < m_oldSize.y) 612 { 613 wxRect rect; 614 rect.y = newSize.y; 615 rect.x = 0; 616 rect.height = 1; 617 rect.width = newSize.x; 618 wxWindowNative::Refresh( true, &rect ); 619 } 620 621 if (newSize.x > m_oldSize.x) 622 { 623 wxRect rect; 624 rect.y = 0; 625 rect.height = m_oldSize.y; 626 rect.x = m_oldSize.x-2; 627 rect.width = 1; 628 Refresh( true, &rect ); 629 } 630 else if (newSize.x < m_oldSize.x) 631 { 632 wxRect rect; 633 rect.x = newSize.x; 634 rect.y = 0; 635 rect.width = 1; 636 rect.height = newSize.y; 637 wxWindowNative::Refresh( true, &rect ); 638 } 639 } 640 else 641 if (HasFlag( wxSUNKEN_BORDER ) || HasFlag( wxRAISED_BORDER )) 642 { 643 if (newSize.y > m_oldSize.y) 644 { 645 wxRect rect; 646 rect.x = 0; 647 rect.width = m_oldSize.x; 648 rect.y = m_oldSize.y-4; 649 rect.height = 2; 650 Refresh( true, &rect ); 651 } 652 else if (newSize.y < m_oldSize.y) 653 { 654 wxRect rect; 655 rect.y = newSize.y; 656 rect.x = 0; 657 rect.height = 2; 658 rect.width = newSize.x; 659 wxWindowNative::Refresh( true, &rect ); 660 } 661 662 if (newSize.x > m_oldSize.x) 663 { 664 wxRect rect; 665 rect.y = 0; 666 rect.height = m_oldSize.y; 667 rect.x = m_oldSize.x-4; 668 rect.width = 2; 669 Refresh( true, &rect ); 670 } 671 else if (newSize.x < m_oldSize.x) 672 { 673 wxRect rect; 674 rect.x = newSize.x; 675 rect.y = 0; 676 rect.width = 2; 677 rect.height = newSize.y; 678 wxWindowNative::Refresh( true, &rect ); 679 } 680 } 681 682 m_oldSize = newSize; 683 } 684#endif 685} 686 687wxSize wxWindow::DoGetBestSize() const 688{ 689 return AdjustSize(DoGetBestClientSize()); 690} 691 692wxSize wxWindow::DoGetBestClientSize() const 693{ 694 return wxWindowNative::DoGetBestSize(); 695} 696 697wxSize wxWindow::AdjustSize(const wxSize& size) const 698{ 699 wxSize sz = size; 700 if ( m_renderer ) 701 m_renderer->AdjustSize(&sz, this); 702 return sz; 703} 704 705wxPoint wxWindow::GetClientAreaOrigin() const 706{ 707 wxPoint pt = wxWindowBase::GetClientAreaOrigin(); 708 709#if wxUSE_TWO_WINDOWS 710#else 711 if ( m_renderer ) 712 pt += m_renderer->GetBorderDimensions(GetBorder()).GetPosition(); 713#endif 714 715 return pt; 716} 717 718void wxWindow::DoGetClientSize(int *width, int *height) const 719{ 720 // if it is a native window, we assume it handles the scrollbars itself 721 // too - and if it doesn't, there is not much we can do 722 if ( !m_renderer ) 723 { 724 wxWindowNative::DoGetClientSize(width, height); 725 726 return; 727 } 728 729 int w, h; 730 wxWindowNative::DoGetClientSize(&w, &h); 731 732 // we assume that the scrollbars are positioned correctly (by a previous 733 // call to PositionScrollbars()) here 734 735 wxRect rectBorder; 736 if ( m_renderer ) 737 rectBorder = m_renderer->GetBorderDimensions(GetBorder()); 738 739 if ( width ) 740 { 741#if wxUSE_SCROLLBAR 742 // in any case, take account of the scrollbar 743 if ( m_scrollbarVert ) 744 w -= m_scrollbarVert->GetSize().x; 745#endif // wxUSE_SCROLLBAR 746 747 // account for the left and right borders 748 *width = w - rectBorder.x - rectBorder.width; 749 750 // we shouldn't return invalid width 751 if ( *width < 0 ) 752 *width = 0; 753 } 754 755 if ( height ) 756 { 757#if wxUSE_SCROLLBAR 758 if ( m_scrollbarHorz ) 759 h -= m_scrollbarHorz->GetSize().y; 760#endif // wxUSE_SCROLLBAR 761 762 *height = h - rectBorder.y - rectBorder.height; 763 764 // we shouldn't return invalid height 765 if ( *height < 0 ) 766 *height = 0; 767 } 768} 769 770void wxWindow::DoSetClientSize(int width, int height) 771{ 772 // take into account the borders 773 wxRect rectBorder = m_renderer->GetBorderDimensions(GetBorder()); 774 width += rectBorder.x; 775 height += rectBorder.y; 776 777 // and the scrollbars (as they may be offset into the border, use the 778 // scrollbar position, not size - this supposes that PositionScrollbars() 779 // had been called before) 780 wxSize size = GetSize(); 781#if wxUSE_SCROLLBAR 782 if ( m_scrollbarVert ) 783 width += size.x - m_scrollbarVert->GetPosition().x; 784#endif // wxUSE_SCROLLBAR 785 width += rectBorder.width; 786 787#if wxUSE_SCROLLBAR 788 if ( m_scrollbarHorz ) 789 height += size.y - m_scrollbarHorz->GetPosition().y; 790#endif // wxUSE_SCROLLBAR 791 height += rectBorder.height; 792 793 wxWindowNative::DoSetClientSize(width, height); 794} 795 796wxHitTest wxWindow::DoHitTest(wxCoord x, wxCoord y) const 797{ 798 wxHitTest ht = wxWindowNative::DoHitTest(x, y); 799 800#if wxUSE_SCROLLBAR 801 if ( ht == wxHT_WINDOW_INSIDE ) 802 { 803 if ( m_scrollbarVert && x >= m_scrollbarVert->GetPosition().x ) 804 { 805 // it can still be changed below because it may also be the corner 806 ht = wxHT_WINDOW_VERT_SCROLLBAR; 807 } 808 809 if ( m_scrollbarHorz && y >= m_scrollbarHorz->GetPosition().y ) 810 { 811 ht = ht == wxHT_WINDOW_VERT_SCROLLBAR ? wxHT_WINDOW_CORNER 812 : wxHT_WINDOW_HORZ_SCROLLBAR; 813 } 814 } 815#endif // wxUSE_SCROLLBAR 816 817 return ht; 818} 819 820// ---------------------------------------------------------------------------- 821// scrolling: we implement it entirely ourselves except for ScrollWindow() 822// function which is supposed to be (efficiently) implemented by the native 823// window class 824// ---------------------------------------------------------------------------- 825 826void wxWindow::RefreshScrollbars() 827{ 828#if wxUSE_SCROLLBAR 829 if ( m_scrollbarHorz ) 830 m_scrollbarHorz->Refresh(); 831 832 if ( m_scrollbarVert ) 833 m_scrollbarVert->Refresh(); 834#endif // wxUSE_SCROLLBAR 835} 836 837void wxWindow::PositionScrollbars() 838{ 839#if wxUSE_SCROLLBAR 840 // do not use GetClientSize/Rect as it relies on the scrollbars being 841 // correctly positioned 842 843 wxSize size = GetSize(); 844 wxBorder border = GetBorder(); 845 wxRect rectBorder = m_renderer->GetBorderDimensions(border); 846 bool inside = m_renderer->AreScrollbarsInsideBorder(); 847 848 int height = m_scrollbarHorz ? m_scrollbarHorz->GetSize().y : 0; 849 int width = m_scrollbarVert ? m_scrollbarVert->GetSize().x : 0; 850 851 wxRect rectBar; 852 if ( m_scrollbarVert ) 853 { 854 rectBar.x = size.x - width; 855 if ( inside ) 856 rectBar.x -= rectBorder.width; 857 rectBar.width = width; 858 rectBar.y = 0; 859 if ( inside ) 860 rectBar.y += rectBorder.y; 861 rectBar.height = size.y - height; 862 if ( inside ) 863 rectBar.height -= rectBorder.y + rectBorder.height; 864 865 m_scrollbarVert->SetSize(rectBar, wxSIZE_NO_ADJUSTMENTS); 866 } 867 868 if ( m_scrollbarHorz ) 869 { 870 rectBar.y = size.y - height; 871 if ( inside ) 872 rectBar.y -= rectBorder.height; 873 rectBar.height = height; 874 rectBar.x = 0; 875 if ( inside ) 876 rectBar.x += rectBorder.x; 877 rectBar.width = size.x - width; 878 if ( inside ) 879 rectBar.width -= rectBorder.x + rectBorder.width; 880 881 m_scrollbarHorz->SetSize(rectBar, wxSIZE_NO_ADJUSTMENTS); 882 } 883 884 RefreshScrollbars(); 885#endif // wxUSE_SCROLLBAR 886} 887 888void wxWindow::SetScrollbar(int orient, 889 int pos, 890 int pageSize, 891 int range, 892 bool refresh) 893{ 894#if wxUSE_SCROLLBAR 895 wxASSERT_MSG( pageSize <= range, 896 _T("page size can't be greater than range") ); 897 898 bool hasClientSizeChanged = false; 899 wxScrollBar *scrollbar = GetScrollbar(orient); 900 if ( range && (pageSize < range) ) 901 { 902 if ( !scrollbar ) 903 { 904 // create it 905#if wxUSE_TWO_WINDOWS 906 SetInsertIntoMain( true ); 907#endif 908 scrollbar = new wxScrollBar(this, wxID_ANY, 909 wxDefaultPosition, wxDefaultSize, 910 orient & wxVERTICAL ? wxSB_VERTICAL 911 : wxSB_HORIZONTAL); 912#if wxUSE_TWO_WINDOWS 913 SetInsertIntoMain( false ); 914#endif 915 if ( orient & wxVERTICAL ) 916 m_scrollbarVert = scrollbar; 917 else 918 m_scrollbarHorz = scrollbar; 919 920 // the client area diminished as we created a scrollbar 921 hasClientSizeChanged = true; 922 923 PositionScrollbars(); 924 } 925 else if ( GetWindowStyle() & wxALWAYS_SHOW_SB ) 926 { 927 // we might have disabled it before 928 scrollbar->Enable(); 929 } 930 931 scrollbar->SetScrollbar(pos, pageSize, range, pageSize, refresh); 932 } 933 else // no range means no scrollbar 934 { 935 if ( scrollbar ) 936 { 937 // wxALWAYS_SHOW_SB only applies to the vertical scrollbar 938 if ( (orient & wxVERTICAL) && (GetWindowStyle() & wxALWAYS_SHOW_SB) ) 939 { 940 // just disable the scrollbar 941 scrollbar->SetScrollbar(pos, pageSize, range, pageSize, refresh); 942 scrollbar->Disable(); 943 } 944 else // really remove the scrollbar 945 { 946 delete scrollbar; 947 948 if ( orient & wxVERTICAL ) 949 m_scrollbarVert = NULL; 950 else 951 m_scrollbarHorz = NULL; 952 953 // the client area increased as we removed a scrollbar 954 hasClientSizeChanged = true; 955 956 // the size of the remaining scrollbar must be adjusted 957 if ( m_scrollbarHorz || m_scrollbarVert ) 958 { 959 PositionScrollbars(); 960 } 961 } 962 } 963 } 964 965 // give the window a chance to relayout 966 if ( hasClientSizeChanged ) 967 { 968#if wxUSE_TWO_WINDOWS 969 wxWindowNative::SetSize( GetSize() ); 970#else 971 wxSizeEvent event(GetSize()); 972 (void)GetEventHandler()->ProcessEvent(event); 973#endif 974 } 975#else 976 wxUnusedVar(orient); 977 wxUnusedVar(pos); 978 wxUnusedVar(pageSize); 979 wxUnusedVar(range); 980 wxUnusedVar(refresh); 981#endif // wxUSE_SCROLLBAR 982} 983 984void wxWindow::SetScrollPos(int orient, int pos, bool WXUNUSED(refresh)) 985{ 986#if wxUSE_SCROLLBAR 987 wxScrollBar *scrollbar = GetScrollbar(orient); 988 989 if (scrollbar) 990 scrollbar->SetThumbPosition(pos); 991 992 // VZ: I think we can safely ignore this as we always refresh it 993 // automatically whenever the value chanegs 994#if 0 995 if ( refresh ) 996 Refresh(); 997#endif 998#else 999 wxUnusedVar(orient); 1000 wxUnusedVar(pos); 1001#endif // wxUSE_SCROLLBAR 1002} 1003 1004int wxWindow::GetScrollPos(int orient) const 1005{ 1006#if wxUSE_SCROLLBAR 1007 wxScrollBar *scrollbar = GetScrollbar(orient); 1008 return scrollbar ? scrollbar->GetThumbPosition() : 0; 1009#else 1010 wxUnusedVar(orient); 1011 return 0; 1012#endif // wxUSE_SCROLLBAR 1013} 1014 1015int wxWindow::GetScrollThumb(int orient) const 1016{ 1017#if wxUSE_SCROLLBAR 1018 wxScrollBar *scrollbar = GetScrollbar(orient); 1019 return scrollbar ? scrollbar->GetThumbSize() : 0; 1020#else 1021 wxUnusedVar(orient); 1022 return 0; 1023#endif // wxUSE_SCROLLBAR 1024} 1025 1026int wxWindow::GetScrollRange(int orient) const 1027{ 1028#if wxUSE_SCROLLBAR 1029 wxScrollBar *scrollbar = GetScrollbar(orient); 1030 return scrollbar ? scrollbar->GetRange() : 0; 1031#else 1032 wxUnusedVar(orient); 1033 return 0; 1034#endif // wxUSE_SCROLLBAR 1035} 1036 1037void wxWindow::ScrollWindow(int dx, int dy, const wxRect *rect) 1038{ 1039 // use native scrolling when available and do it in generic way 1040 // otherwise: 1041#ifdef __WXX11__ 1042 1043 wxWindowNative::ScrollWindow(dx, dy, rect); 1044 1045#else // !wxX11 1046 1047 // before scrolling it, ensure that we don't have any unpainted areas 1048 Update(); 1049 1050 wxRect r; 1051 1052 if ( dx ) 1053 { 1054 r = ScrollNoRefresh(dx, 0, rect); 1055 Refresh(true /* erase bkgnd */, &r); 1056 } 1057 1058 if ( dy ) 1059 { 1060 r = ScrollNoRefresh(0, dy, rect); 1061 Refresh(true /* erase bkgnd */, &r); 1062 } 1063 1064 // scroll children accordingly: 1065 wxPoint offset(dx, dy); 1066 1067 for (wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); 1068 node; node = node->GetNext()) 1069 { 1070 wxWindow *child = node->GetData(); 1071#if wxUSE_SCROLLBAR 1072 if ( child == m_scrollbarVert || child == m_scrollbarHorz ) 1073 continue; 1074#endif // wxUSE_SCROLLBAR 1075 1076 // VS: Scrolling children has non-trivial semantics. If rect=NULL then 1077 // it is easy: we scroll all children. Otherwise it gets 1078 // complicated: 1079 // 1. if scrolling in one direction only, scroll only 1080 // those children that intersect shaft defined by the rectangle 1081 // and scrolling direction 1082 // 2. if scrolling in both axes, scroll all children 1083 1084 bool shouldMove = false; 1085 1086 if ( rect && (dx * dy == 0 /* moving in only one of x, y axis */) ) 1087 { 1088 wxRect childRect = child->GetRect(); 1089 if ( dx == 0 && (childRect.GetLeft() <= rect->GetRight() || 1090 childRect.GetRight() >= rect->GetLeft()) ) 1091 { 1092 shouldMove = true; 1093 } 1094 else if ( dy == 0 && (childRect.GetTop() <= rect->GetBottom() || 1095 childRect.GetBottom() >= rect->GetTop()) ) 1096 { 1097 shouldMove = true; 1098 } 1099 // else: child outside of scrolling shaft, don't move 1100 } 1101 else // scrolling in both axes or rect=NULL 1102 { 1103 shouldMove = true; 1104 } 1105 1106 if ( shouldMove ) 1107 child->Move(child->GetPosition() + offset, wxSIZE_ALLOW_MINUS_ONE); 1108 } 1109#endif // wxX11/!wxX11 1110} 1111 1112wxRect wxWindow::ScrollNoRefresh(int dx, int dy, const wxRect *rectTotal) 1113{ 1114 wxASSERT_MSG( !dx || !dy, _T("can't be used for diag scrolling") ); 1115 1116 // the rect to refresh (which we will calculate) 1117 wxRect rect; 1118 1119 if ( !dx && !dy ) 1120 { 1121 // nothing to do 1122 return rect; 1123 } 1124 1125 // calculate the part of the window which we can just redraw in the new 1126 // location 1127 wxSize sizeTotal = rectTotal ? rectTotal->GetSize() : GetClientSize(); 1128 1129 wxLogTrace(_T("scroll"), _T("rect is %dx%d, scroll by %d, %d"), 1130 sizeTotal.x, sizeTotal.y, dx, dy); 1131 1132 // the initial and end point of the region we move in client coords 1133 wxPoint ptSource, ptDest; 1134 if ( rectTotal ) 1135 { 1136 ptSource = rectTotal->GetPosition(); 1137 ptDest = rectTotal->GetPosition(); 1138 } 1139 1140 // the size of this region 1141 wxSize size; 1142 size.x = sizeTotal.x - abs(dx); 1143 size.y = sizeTotal.y - abs(dy); 1144 if ( size.x <= 0 || size.y <= 0 ) 1145 { 1146 // just redraw everything as nothing of the displayed image will stay 1147 wxLogTrace(_T("scroll"), _T("refreshing everything")); 1148 1149 rect = rectTotal ? *rectTotal : wxRect(0, 0, sizeTotal.x, sizeTotal.y); 1150 } 1151 else // move the part which doesn't change to the new location 1152 { 1153 // note that when we scroll the canvas in some direction we move the 1154 // block which doesn't need to be refreshed in the opposite direction 1155 1156 if ( dx < 0 ) 1157 { 1158 // scroll to the right, move to the left 1159 ptSource.x -= dx; 1160 } 1161 else 1162 { 1163 // scroll to the left, move to the right 1164 ptDest.x += dx; 1165 } 1166 1167 if ( dy < 0 ) 1168 { 1169 // scroll down, move up 1170 ptSource.y -= dy; 1171 } 1172 else 1173 { 1174 // scroll up, move down 1175 ptDest.y += dy; 1176 } 1177 1178#if wxUSE_CARET 1179 // we need to hide the caret before moving or it will erase itself at 1180 // the wrong (old) location 1181 wxCaret *caret = GetCaret(); 1182 if ( caret ) 1183 caret->Hide(); 1184#endif // wxUSE_CARET 1185 1186 // do move 1187 wxClientDC dc(this); 1188 wxBitmap bmp(size.x, size.y); 1189 wxMemoryDC dcMem; 1190 dcMem.SelectObject(bmp); 1191 1192 dcMem.Blit(wxPoint(0,0), size, &dc, ptSource 1193#if defined(__WXGTK__) && !defined(wxHAS_WORKING_GTK_DC_BLIT) 1194 + GetClientAreaOrigin() 1195#endif // broken wxGTK wxDC::Blit 1196 ); 1197 dc.Blit(ptDest, size, &dcMem, wxPoint(0,0)); 1198 1199 wxLogTrace(_T("scroll"), 1200 _T("Blit: (%d, %d) of size %dx%d -> (%d, %d)"), 1201 ptSource.x, ptSource.y, 1202 size.x, size.y, 1203 ptDest.x, ptDest.y); 1204 1205 // and now repaint the uncovered area 1206 1207 // FIXME: We repaint the intersection of these rectangles twice - is 1208 // it bad? I don't think so as it is rare to scroll the window 1209 // diagonally anyhow and so adding extra logic to compute 1210 // rectangle intersection is probably not worth the effort 1211 1212 rect.x = ptSource.x; 1213 rect.y = ptSource.y; 1214 1215 if ( dx ) 1216 { 1217 if ( dx < 0 ) 1218 { 1219 // refresh the area along the right border 1220 rect.x += size.x + dx; 1221 rect.width = -dx; 1222 } 1223 else 1224 { 1225 // refresh the area along the left border 1226 rect.width = dx; 1227 } 1228 1229 rect.height = sizeTotal.y; 1230 1231 wxLogTrace(_T("scroll"), _T("refreshing (%d, %d)-(%d, %d)"), 1232 rect.x, rect.y, 1233 rect.GetRight() + 1, rect.GetBottom() + 1); 1234 } 1235 1236 if ( dy ) 1237 { 1238 if ( dy < 0 ) 1239 { 1240 // refresh the area along the bottom border 1241 rect.y += size.y + dy; 1242 rect.height = -dy; 1243 } 1244 else 1245 { 1246 // refresh the area along the top border 1247 rect.height = dy; 1248 } 1249 1250 rect.width = sizeTotal.x; 1251 1252 wxLogTrace(_T("scroll"), _T("refreshing (%d, %d)-(%d, %d)"), 1253 rect.x, rect.y, 1254 rect.GetRight() + 1, rect.GetBottom() + 1); 1255 } 1256 1257#if wxUSE_CARET 1258 if ( caret ) 1259 caret->Show(); 1260#endif // wxUSE_CARET 1261 } 1262 1263 return rect; 1264} 1265 1266// ---------------------------------------------------------------------------- 1267// accelerators and menu hot keys 1268// ---------------------------------------------------------------------------- 1269 1270#if wxUSE_MENUS 1271 // the last window over which Alt was pressed (used by OnKeyUp) 1272 wxWindow *wxWindow::ms_winLastAltPress = NULL; 1273#endif // wxUSE_MENUS 1274 1275#if wxUSE_ACCEL || wxUSE_MENUS 1276 1277void wxWindow::OnKeyDown(wxKeyEvent& event) 1278{ 1279#if wxUSE_MENUS 1280 int key = event.GetKeyCode(); 1281 if ( !event.ControlDown() && (key == WXK_ALT || key == WXK_F10) ) 1282 { 1283 ms_winLastAltPress = this; 1284 1285 // it can't be an accel anyhow 1286 return; 1287 } 1288 1289 ms_winLastAltPress = NULL; 1290#endif // wxUSE_MENUS 1291 1292#if wxUSE_ACCEL 1293 for ( wxWindow *win = this; win; win = win->GetParent() ) 1294 { 1295 int command = win->GetAcceleratorTable()->GetCommand(event); 1296 if ( command != -1 ) 1297 { 1298 wxCommandEvent eventCmd(wxEVT_COMMAND_MENU_SELECTED, command); 1299 if ( win->GetEventHandler()->ProcessEvent(eventCmd) ) 1300 { 1301 // skip "event.Skip()" below 1302 return; 1303 } 1304 } 1305 1306 if ( win->IsTopLevel() ) 1307 { 1308 // try the frame menu bar 1309#if wxUSE_MENUS 1310 wxFrame *frame = wxDynamicCast(win, wxFrame); 1311 if ( frame ) 1312 { 1313 wxMenuBar *menubar = frame->GetMenuBar(); 1314 if ( menubar && menubar->ProcessAccelEvent(event) ) 1315 { 1316 // skip "event.Skip()" below 1317 return; 1318 } 1319 } 1320#endif // wxUSE_MENUS 1321 1322#if wxUSE_BUTTON 1323 // if it wasn't in a menu, try to find a button 1324 if ( command != -1 ) 1325 { 1326 wxWindow* child = win->FindWindow(command); 1327 if ( child && wxDynamicCast(child, wxButton) ) 1328 { 1329 wxCommandEvent eventCmd(wxEVT_COMMAND_BUTTON_CLICKED, command); 1330 eventCmd.SetEventObject(child); 1331 if ( child->GetEventHandler()->ProcessEvent(eventCmd) ) 1332 { 1333 // skip "event.Skip()" below 1334 return; 1335 } 1336 } 1337 } 1338#endif // wxUSE_BUTTON 1339 1340 // don't propagate accels from the child frame to the parent one 1341 break; 1342 } 1343 } 1344#endif // wxUSE_ACCEL 1345 1346 event.Skip(); 1347} 1348 1349#endif // wxUSE_ACCEL 1350 1351#if wxUSE_MENUS 1352 1353wxMenuBar *wxWindow::GetParentFrameMenuBar() const 1354{ 1355 for ( const wxWindow *win = this; win; win = win->GetParent() ) 1356 { 1357 if ( win->IsTopLevel() ) 1358 { 1359 wxFrame *frame = wxDynamicCast(win, wxFrame); 1360 if ( frame ) 1361 { 1362 return frame->GetMenuBar(); 1363 } 1364 1365 // don't look further - we don't want to return the menubar of the 1366 // parent frame 1367 break; 1368 } 1369 } 1370 1371 return NULL; 1372} 1373 1374void wxWindow::OnChar(wxKeyEvent& event) 1375{ 1376 if ( event.AltDown() && !event.ControlDown() ) 1377 { 1378 int key = event.GetKeyCode(); 1379 1380 wxMenuBar *menubar = GetParentFrameMenuBar(); 1381 if ( menubar ) 1382 { 1383 int item = menubar->FindNextItemForAccel(-1, key); 1384 if ( item != -1 ) 1385 { 1386 menubar->PopupMenu((size_t)item); 1387 1388 // skip "event.Skip()" below 1389 return; 1390 } 1391 } 1392 } 1393 1394 event.Skip(); 1395} 1396 1397void wxWindow::OnKeyUp(wxKeyEvent& event) 1398{ 1399 int key = event.GetKeyCode(); 1400 if ( !event.HasModifiers() && (key == WXK_ALT || key == WXK_F10) ) 1401 { 1402 // only process Alt release specially if there were no other key 1403 // presses since Alt had been pressed and if both events happened in 1404 // the same window 1405 if ( ms_winLastAltPress == this ) 1406 { 1407 wxMenuBar *menubar = GetParentFrameMenuBar(); 1408 if ( menubar && this != menubar ) 1409 { 1410 menubar->SelectMenu(0); 1411 } 1412 } 1413 } 1414 else 1415 { 1416 event.Skip(); 1417 } 1418 1419 // in any case reset it 1420 ms_winLastAltPress = NULL; 1421} 1422 1423#endif // wxUSE_MENUS 1424 1425// ---------------------------------------------------------------------------- 1426// MSW-specific section 1427// ---------------------------------------------------------------------------- 1428 1429#ifdef __WXMSW__ 1430 1431#include "wx/msw/private.h" 1432 1433WXLRESULT wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) 1434{ 1435 if ( message == WM_NCHITTEST ) 1436 { 1437 // the windows which contain the other windows should let the mouse 1438 // events through, otherwise a window inside a static box would 1439 // never get any events at all 1440 if ( IsStaticBox() ) 1441 { 1442 return HTTRANSPARENT; 1443 } 1444 } 1445 1446 return wxWindowNative::MSWWindowProc(message, wParam, lParam); 1447} 1448 1449#endif // __WXMSW__ 1450