Files
wxWidgets/src/univ/winuniv.cpp
Vadim Zeitlin 3f66f6a5b3 Remove all lines containing cvs/svn "$Id$" keyword.
This keyword is not expanded by Git which means it's not replaced with the
correct revision value in the releases made using git-based scripts and it's
confusing to have lines with unexpanded "$Id$" in the released files. As
expanding them with Git is not that simple (it could be done with git archive
and export-subst attribute) and there are not many benefits in having them in
the first place, just remove all these lines.

If nothing else, this will make an eventual transition to Git simpler.

Closes #14487.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74602 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2013-07-26 16:02:46 +00:00

1495 lines
41 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/univ/winuniv.cpp
// Purpose: implementation of extra wxWindow methods for wxUniv port
// Author: Vadim Zeitlin
// Modified by:
// Created: 06.08.00
// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ===========================================================================
// declarations
// ===========================================================================
// ---------------------------------------------------------------------------
// headers
// ---------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#include "wx/window.h"
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/dcclient.h"
#include "wx/dcmemory.h"
#include "wx/event.h"
#include "wx/scrolbar.h"
#include "wx/menu.h"
#include "wx/frame.h"
#include "wx/log.h"
#include "wx/button.h"
#endif // WX_PRECOMP
#include "wx/univ/colschem.h"
#include "wx/univ/renderer.h"
#include "wx/univ/theme.h"
#if wxUSE_CARET
#include "wx/caret.h"
#endif // wxUSE_CARET
#if wxDEBUG_LEVEL >= 2
// turn Refresh() debugging on/off
#define WXDEBUG_REFRESH
#endif
#if defined(WXDEBUG_REFRESH) && defined(__WXMSW__) && !defined(__WXMICROWIN__)
#include "wx/msw/private.h"
#endif
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// scrollbars class
// ----------------------------------------------------------------------------
// This is scrollbar class used to implement wxWindow's "built-in" scrollbars;
// unlike the standard wxScrollBar class, this one is positioned outside of its
// parent's client area
class wxWindowScrollBar : public wxScrollBar
{
public:
wxWindowScrollBar(wxWindow *parent,
wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxSB_HORIZONTAL)
: wxScrollBar(parent, id, pos, size, style)
{
}
virtual bool CanBeOutsideClientArea() const { return true; }
};
// ----------------------------------------------------------------------------
// event tables
// ----------------------------------------------------------------------------
// we can't use wxWindowNative here as it won't be expanded inside the macro
#if defined(__WXMSW__)
IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowMSW)
#elif defined(__WXGTK__)
IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowGTK)
#elif defined(__WXOSX_OR_COCOA__)
IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowMac)
#elif defined(__WXDFB__)
IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowDFB)
#elif defined(__WXX11__)
IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowX11)
#elif defined(__WXPM__)
IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowOS2)
#endif
BEGIN_EVENT_TABLE(wxWindow, wxWindowNative)
EVT_SIZE(wxWindow::OnSize)
#if wxUSE_ACCEL || wxUSE_MENUS
EVT_KEY_DOWN(wxWindow::OnKeyDown)
#endif // wxUSE_ACCEL
#if wxUSE_MENUS
EVT_CHAR(wxWindow::OnChar)
EVT_KEY_UP(wxWindow::OnKeyUp)
#endif // wxUSE_MENUS
EVT_PAINT(wxWindow::OnPaint)
EVT_NC_PAINT(wxWindow::OnNcPaint)
EVT_ERASE_BACKGROUND(wxWindow::OnErase)
END_EVENT_TABLE()
// ----------------------------------------------------------------------------
// creation
// ----------------------------------------------------------------------------
void wxWindow::Init()
{
#if wxUSE_SCROLLBAR
m_scrollbarVert =
m_scrollbarHorz = NULL;
#endif // wxUSE_SCROLLBAR
m_isCurrent = false;
m_renderer = wxTheme::Get()->GetRenderer();
m_oldSize.x = wxDefaultCoord;
m_oldSize.y = wxDefaultCoord;
}
bool wxWindow::Create(wxWindow *parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
{
// Get default border
wxBorder border = GetBorder(style);
style &= ~wxBORDER_MASK;
style |= border;
long actualStyle = style;
// we add wxCLIP_CHILDREN to get the same ("natural") behaviour under MSW
// as under the other platforms
actualStyle |= wxCLIP_CHILDREN;
actualStyle &= ~wxVSCROLL;
actualStyle &= ~wxHSCROLL;
#ifdef __WXMSW__
// without this, borders (non-client areas in general) are not repainted
// correctly when resizing; apparently, native NC areas are fully repainted
// even without this style by MSW, but wxUniv implements client area
// itself, so it doesn't work correctly for us
//
// FIXME: this is very expensive, we need to fix the (commented-out) code
// in OnSize() instead
actualStyle |= wxFULL_REPAINT_ON_RESIZE;
#endif
if ( !wxWindowNative::Create(parent, id, pos, size, actualStyle, name) )
return false;
// Set full style again, including those we didn't want present
// when calling the base window Create().
wxWindowBase::SetWindowStyleFlag(style);
// if we allow or should always have a vertical scrollbar, make it
if ( style & wxVSCROLL || style & wxALWAYS_SHOW_SB )
{
#if wxUSE_TWO_WINDOWS
SetInsertIntoMain( true );
#endif
#if wxUSE_SCROLLBAR
m_scrollbarVert = new wxWindowScrollBar(this, wxID_ANY,
wxDefaultPosition, wxDefaultSize,
wxSB_VERTICAL);
#endif // wxUSE_SCROLLBAR
#if wxUSE_TWO_WINDOWS
SetInsertIntoMain( false );
#endif
}
// if we should allow a horizontal scrollbar, make it
if ( style & wxHSCROLL )
{
#if wxUSE_TWO_WINDOWS
SetInsertIntoMain( true );
#endif
#if wxUSE_SCROLLBAR
m_scrollbarHorz = new wxWindowScrollBar(this, wxID_ANY,
wxDefaultPosition, wxDefaultSize,
wxSB_HORIZONTAL);
#endif // wxUSE_SCROLLBAR
#if wxUSE_TWO_WINDOWS
SetInsertIntoMain( false );
#endif
}
#if wxUSE_SCROLLBAR
if (m_scrollbarHorz || m_scrollbarVert)
{
// position it/them
PositionScrollbars();
}
#endif // wxUSE_SCROLLBAR
return true;
}
wxWindow::~wxWindow()
{
SendDestroyEvent();
#if wxUSE_SCROLLBAR
// clear pointers to scrollbar before deleting the children: they are
// children and so will be deleted by DestroyChildren() call below and if
// any code using the scrollbars would be called in the process or from
// ~wxWindowBase, the app would crash:
m_scrollbarVert = m_scrollbarHorz = NULL;
#endif
// we have to destroy our children before we're destroyed because our
// children suppose that we're of type wxWindow, not just wxWindowNative,
// and so bad things may happen if they're deleted from the base class dtor
// as by then we're not a wxWindow any longer and wxUniv-specific virtual
// functions can't be called
DestroyChildren();
}
// ----------------------------------------------------------------------------
// background pixmap
// ----------------------------------------------------------------------------
void wxWindow::SetBackground(const wxBitmap& bitmap,
int alignment,
wxStretch stretch)
{
m_bitmapBg = bitmap;
m_alignBgBitmap = alignment;
m_stretchBgBitmap = stretch;
}
const wxBitmap& wxWindow::GetBackgroundBitmap(int *alignment,
wxStretch *stretch) const
{
if ( m_bitmapBg.IsOk() )
{
if ( alignment )
*alignment = m_alignBgBitmap;
if ( stretch )
*stretch = m_stretchBgBitmap;
}
return m_bitmapBg;
}
// ----------------------------------------------------------------------------
// painting
// ----------------------------------------------------------------------------
// the event handlers executed when the window must be repainted
void wxWindow::OnNcPaint(wxNcPaintEvent& WXUNUSED(event))
{
if ( m_renderer )
{
// get the window rect
wxRect rect(GetSize());
#if wxUSE_SCROLLBAR
// if the scrollbars are outside the border, we must adjust the rect to
// exclude them
if ( !m_renderer->AreScrollbarsInsideBorder() )
{
wxScrollBar *scrollbar = GetScrollbar(wxVERTICAL);
if ( scrollbar )
rect.width -= scrollbar->GetSize().x;
scrollbar = GetScrollbar(wxHORIZONTAL);
if ( scrollbar )
rect.height -= scrollbar->GetSize().y;
}
#endif // wxUSE_SCROLLBAR
// get the DC and draw the border on it
wxWindowDC dc(this);
DoDrawBorder(dc, rect);
}
}
void wxWindow::OnPaint(wxPaintEvent& event)
{
if ( !m_renderer )
{
// it is a native control which paints itself
event.Skip();
}
else
{
// get the DC to use and create renderer on it
wxPaintDC dc(this);
wxControlRenderer renderer(this, dc, m_renderer);
// draw the control
DoDraw(&renderer);
}
}
// the event handler executed when the window background must be painted
void wxWindow::OnErase(wxEraseEvent& event)
{
if ( !m_renderer )
{
event.Skip();
return;
}
DoDrawBackground(*event.GetDC());
#if wxUSE_SCROLLBAR
// if we have both scrollbars, we also have a square in the corner between
// them which we must paint
if ( m_scrollbarVert && m_scrollbarHorz )
{
wxSize size = GetSize();
wxRect rectClient = GetClientRect(),
rectBorder = m_renderer->GetBorderDimensions(GetBorder());
wxRect rectCorner;
rectCorner.x = rectClient.GetRight() + 1;
rectCorner.y = rectClient.GetBottom() + 1;
rectCorner.SetRight(size.x - rectBorder.width);
rectCorner.SetBottom(size.y - rectBorder.height);
if ( GetUpdateRegion().Contains(rectCorner) )
{
m_renderer->DrawScrollCorner(*event.GetDC(), rectCorner);
}
}
#endif // wxUSE_SCROLLBAR
}
bool wxWindow::DoDrawBackground(wxDC& dc)
{
wxRect rect;
wxSize size = GetSize(); // Why not GetClientSize() ?
rect.x = 0;
rect.y = 0;
rect.width = size.x;
rect.height = size.y;
wxWindow * const parent = GetParent();
if ( HasTransparentBackground() && !UseBgCol() && parent )
{
// DirectFB paints the parent first, then its child windows, so by
// the time this code is called, parent's background was already
// drawn and there's no point in (imperfectly!) duplicating the work
// here:
#ifndef __WXDFB__
wxASSERT( !IsTopLevel() );
wxPoint pos = GetPosition();
AdjustForParentClientOrigin( pos.x, pos.y, 0 );
// Adjust DC logical origin
wxCoord org_x, org_y, x, y;
dc.GetLogicalOrigin( &org_x, &org_y );
x = org_x + pos.x;
y = org_y + pos.y;
dc.SetLogicalOrigin( x, y );
// Adjust draw rect
rect.x = pos.x;
rect.y = pos.y;
// Let parent draw the background
parent->EraseBackground( dc, rect );
// Restore DC logical origin
dc.SetLogicalOrigin( org_x, org_y );
#endif // !__WXDFB__
}
else
{
// Draw background ourselves
EraseBackground( dc, rect );
}
return true;
}
void wxWindow::EraseBackground(wxDC& dc, const wxRect& rect)
{
if ( GetBackgroundBitmap().IsOk() )
{
// Get the bitmap and the flags
int alignment;
wxStretch stretch;
wxBitmap bmp = GetBackgroundBitmap(&alignment, &stretch);
wxControlRenderer::DrawBitmap(dc, bmp, rect, alignment, stretch);
}
else
{
// Just fill it with bg colour if no bitmap
m_renderer->DrawBackground(dc, wxTHEME_BG_COLOUR(this),
rect, GetStateFlags());
}
}
void wxWindow::DoDrawBorder(wxDC& dc, const wxRect& rect)
{
// draw outline unless the update region is enitrely inside it in which
// case we don't need to do it
#if 0 // doesn't seem to work, why?
if ( wxRegion(rect).Contains(GetUpdateRegion().GetBox()) != wxInRegion )
#endif
{
m_renderer->DrawBorder(dc, GetBorder(), rect, GetStateFlags());
}
}
void wxWindow::DoDraw(wxControlRenderer * WXUNUSED(renderer))
{
}
void wxWindow::Refresh(bool eraseBackground, const wxRect *rect)
{
wxRect rectClient; // the same rectangle in client coordinates
wxPoint origin = GetClientAreaOrigin();
wxSize size = GetClientSize();
if ( rect )
{
// the rectangle passed as argument is in client coordinates
rectClient = *rect;
// don't refresh anything beyond the client area (scrollbars for
// example)
if ( rectClient.GetRight() > size.x )
rectClient.SetRight(size.x);
if ( rectClient.GetBottom() > size.y )
rectClient.SetBottom(size.y);
}
else // refresh the entire client area
{
// x,y is already set to 0 by default
rectClient.SetSize(size);
}
// convert refresh rectangle to window coordinates:
wxRect rectWin(rectClient);
rectWin.Offset(origin);
// debugging helper
#ifdef WXDEBUG_REFRESH
static bool s_refreshDebug = false;
if ( s_refreshDebug )
{
wxWindowDC dc(this);
dc.SetBrush(*wxCYAN_BRUSH);
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRectangle(rectWin);
// under Unix we use "--sync" X option for this
#if defined(__WXMSW__) && !defined(__WXMICROWIN__)
::GdiFlush();
::Sleep(200);
#endif // __WXMSW__
}
#endif // WXDEBUG_REFRESH
wxWindowNative::Refresh(eraseBackground, &rectWin);
// Refresh all sub controls if any.
wxWindowList& children = GetChildren();
for ( wxWindowList::iterator i = children.begin(); i != children.end(); ++i )
{
wxWindow *child = *i;
// only refresh subcontrols if they are visible:
if ( child->IsTopLevel() || !child->IsShown() || child->IsFrozen() )
continue;
// ...and when the subcontrols are in the update region:
wxRect childrect(child->GetRect());
childrect.Intersect(rectClient);
if ( childrect.IsEmpty() )
continue;
// refresh the subcontrol now:
childrect.Offset(-child->GetPosition());
// NB: We must call wxWindowNative version because we need to refresh
// the entire control, not just its client area, and this is why we
// don't account for child client area origin here neither. Also
// note that we don't pass eraseBackground to the child, but use
// true instead: this is because we can't be sure that
// eraseBackground=false is safe for children as well and not only
// for the parent.
child->wxWindowNative::Refresh(eraseBackground, &childrect);
}
}
// ----------------------------------------------------------------------------
// state flags
// ----------------------------------------------------------------------------
bool wxWindow::Enable(bool enable)
{
if ( !wxWindowNative::Enable(enable) )
return false;
// disabled window can't keep focus
if ( FindFocus() == this && GetParent() != NULL )
{
GetParent()->SetFocus();
}
if ( m_renderer )
{
// a window with renderer is drawn by ourselves and it has to be
// refreshed to reflect its new status
Refresh();
}
return true;
}
bool wxWindow::IsFocused() const
{
return FindFocus() == this;
}
bool wxWindow::IsPressed() const
{
return false;
}
bool wxWindow::IsDefault() const
{
return false;
}
bool wxWindow::IsCurrent() const
{
return m_isCurrent;
}
bool wxWindow::SetCurrent(bool doit)
{
if ( doit == m_isCurrent )
return false;
m_isCurrent = doit;
if ( CanBeHighlighted() )
Refresh();
return true;
}
int wxWindow::GetStateFlags() const
{
int flags = 0;
if ( !IsEnabled() )
flags |= wxCONTROL_DISABLED;
// the following states are only possible if our application is active - if
// it is not, even our default/focused controls shouldn't appear as such
if ( wxTheApp->IsActive() )
{
if ( IsCurrent() )
flags |= wxCONTROL_CURRENT;
if ( IsFocused() )
flags |= wxCONTROL_FOCUSED;
if ( IsPressed() )
flags |= wxCONTROL_PRESSED;
if ( IsDefault() )
flags |= wxCONTROL_ISDEFAULT;
}
return flags;
}
// ----------------------------------------------------------------------------
// size
// ----------------------------------------------------------------------------
void wxWindow::OnSize(wxSizeEvent& event)
{
event.Skip();
#if wxUSE_SCROLLBAR
if ( m_scrollbarVert || m_scrollbarHorz )
{
PositionScrollbars();
}
#endif // wxUSE_SCROLLBAR
#if 0 // ndef __WXMSW__
// Refresh the area (strip) previously occupied by the border
if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) && IsShown() )
{
// This code assumes that wxSizeEvent.GetSize() returns
// the area of the entire window, not just the client
// area.
wxSize newSize = event.GetSize();
if (m_oldSize.x == wxDefaultCoord && m_oldSize.y == wxDefaultCoord)
{
m_oldSize = newSize;
return;
}
if (HasFlag( wxSIMPLE_BORDER ))
{
if (newSize.y > m_oldSize.y)
{
wxRect rect;
rect.x = 0;
rect.width = m_oldSize.x;
rect.y = m_oldSize.y-2;
rect.height = 1;
Refresh( true, &rect );
}
else if (newSize.y < m_oldSize.y)
{
wxRect rect;
rect.y = newSize.y;
rect.x = 0;
rect.height = 1;
rect.width = newSize.x;
wxWindowNative::Refresh( true, &rect );
}
if (newSize.x > m_oldSize.x)
{
wxRect rect;
rect.y = 0;
rect.height = m_oldSize.y;
rect.x = m_oldSize.x-2;
rect.width = 1;
Refresh( true, &rect );
}
else if (newSize.x < m_oldSize.x)
{
wxRect rect;
rect.x = newSize.x;
rect.y = 0;
rect.width = 1;
rect.height = newSize.y;
wxWindowNative::Refresh( true, &rect );
}
}
else
if (HasFlag( wxSUNKEN_BORDER ) || HasFlag( wxRAISED_BORDER ) || HasFlag( wxBORDER_THEME ))
{
if (newSize.y > m_oldSize.y)
{
wxRect rect;
rect.x = 0;
rect.width = m_oldSize.x;
rect.y = m_oldSize.y-4;
rect.height = 2;
Refresh( true, &rect );
}
else if (newSize.y < m_oldSize.y)
{
wxRect rect;
rect.y = newSize.y;
rect.x = 0;
rect.height = 2;
rect.width = newSize.x;
wxWindowNative::Refresh( true, &rect );
}
if (newSize.x > m_oldSize.x)
{
wxRect rect;
rect.y = 0;
rect.height = m_oldSize.y;
rect.x = m_oldSize.x-4;
rect.width = 2;
Refresh( true, &rect );
}
else if (newSize.x < m_oldSize.x)
{
wxRect rect;
rect.x = newSize.x;
rect.y = 0;
rect.width = 2;
rect.height = newSize.y;
wxWindowNative::Refresh( true, &rect );
}
}
m_oldSize = newSize;
}
#endif
}
wxSize wxWindow::DoGetBorderSize() const
{
return AdjustSize(wxSize(0, 0));
}
wxSize wxWindow::AdjustSize(const wxSize& size) const
{
wxSize sz = size;
if ( m_renderer )
m_renderer->AdjustSize(&sz, this);
return sz;
}
wxPoint wxWindow::GetClientAreaOrigin() const
{
wxPoint pt = wxWindowBase::GetClientAreaOrigin();
#if wxUSE_TWO_WINDOWS
#else
if ( m_renderer )
pt += m_renderer->GetBorderDimensions(GetBorder()).GetPosition();
#endif
return pt;
}
void wxWindow::DoGetClientSize(int *width, int *height) const
{
// if it is a native window, we assume it handles the scrollbars itself
// too - and if it doesn't, there is not much we can do
if ( !m_renderer )
{
wxWindowNative::DoGetClientSize(width, height);
return;
}
int w, h;
wxWindowNative::DoGetClientSize(&w, &h);
// we assume that the scrollbars are positioned correctly (by a previous
// call to PositionScrollbars()) here
wxRect rectBorder;
if ( m_renderer )
rectBorder = m_renderer->GetBorderDimensions(GetBorder());
if ( width )
{
#if wxUSE_SCROLLBAR
// in any case, take account of the scrollbar
if ( m_scrollbarVert )
w -= m_scrollbarVert->GetSize().x;
#endif // wxUSE_SCROLLBAR
// account for the left and right borders
*width = w - rectBorder.x - rectBorder.width;
// we shouldn't return invalid width
if ( *width < 0 )
*width = 0;
}
if ( height )
{
#if wxUSE_SCROLLBAR
if ( m_scrollbarHorz )
h -= m_scrollbarHorz->GetSize().y;
#endif // wxUSE_SCROLLBAR
*height = h - rectBorder.y - rectBorder.height;
// we shouldn't return invalid height
if ( *height < 0 )
*height = 0;
}
}
void wxWindow::DoSetClientSize(int width, int height)
{
// take into account the borders
wxRect rectBorder = m_renderer->GetBorderDimensions(GetBorder());
width += rectBorder.x;
height += rectBorder.y;
// and the scrollbars (as they may be offset into the border, use the
// scrollbar position, not size - this supposes that PositionScrollbars()
// had been called before)
wxSize size = GetSize();
#if wxUSE_SCROLLBAR
if ( m_scrollbarVert )
width += size.x - m_scrollbarVert->GetPosition().x;
#endif // wxUSE_SCROLLBAR
width += rectBorder.width;
#if wxUSE_SCROLLBAR
if ( m_scrollbarHorz )
height += size.y - m_scrollbarHorz->GetPosition().y;
#endif // wxUSE_SCROLLBAR
height += rectBorder.height;
wxWindowNative::DoSetClientSize(width, height);
}
wxHitTest wxWindow::DoHitTest(wxCoord x, wxCoord y) const
{
wxHitTest ht = wxWindowNative::DoHitTest(x, y);
#if wxUSE_SCROLLBAR
if ( ht == wxHT_WINDOW_INSIDE )
{
if ( m_scrollbarVert && x >= m_scrollbarVert->GetPosition().x )
{
// it can still be changed below because it may also be the corner
ht = wxHT_WINDOW_VERT_SCROLLBAR;
}
if ( m_scrollbarHorz && y >= m_scrollbarHorz->GetPosition().y )
{
ht = ht == wxHT_WINDOW_VERT_SCROLLBAR ? wxHT_WINDOW_CORNER
: wxHT_WINDOW_HORZ_SCROLLBAR;
}
}
#endif // wxUSE_SCROLLBAR
return ht;
}
// ----------------------------------------------------------------------------
// scrolling: we implement it entirely ourselves except for ScrollWindow()
// function which is supposed to be (efficiently) implemented by the native
// window class
// ----------------------------------------------------------------------------
void wxWindow::RefreshScrollbars()
{
#if wxUSE_SCROLLBAR
if ( m_scrollbarHorz )
m_scrollbarHorz->Refresh();
if ( m_scrollbarVert )
m_scrollbarVert->Refresh();
#endif // wxUSE_SCROLLBAR
}
void wxWindow::PositionScrollbars()
{
#if wxUSE_SCROLLBAR
// do not use GetClientSize/Rect as it relies on the scrollbars being
// correctly positioned
wxSize size = GetSize();
wxBorder border = GetBorder();
wxRect rectBorder = m_renderer->GetBorderDimensions(border);
bool inside = m_renderer->AreScrollbarsInsideBorder();
int height = m_scrollbarHorz ? m_scrollbarHorz->GetSize().y : 0;
int width = m_scrollbarVert ? m_scrollbarVert->GetSize().x : 0;
wxRect rectBar;
if ( m_scrollbarVert )
{
rectBar.x = size.x - width;
if ( inside )
rectBar.x -= rectBorder.width;
rectBar.width = width;
rectBar.y = 0;
if ( inside )
rectBar.y += rectBorder.y;
rectBar.height = size.y - height;
if ( inside )
rectBar.height -= rectBorder.y + rectBorder.height;
m_scrollbarVert->SetSize(rectBar, wxSIZE_NO_ADJUSTMENTS);
}
if ( m_scrollbarHorz )
{
rectBar.y = size.y - height;
if ( inside )
rectBar.y -= rectBorder.height;
rectBar.height = height;
rectBar.x = 0;
if ( inside )
rectBar.x += rectBorder.x;
rectBar.width = size.x - width;
if ( inside )
rectBar.width -= rectBorder.x + rectBorder.width;
m_scrollbarHorz->SetSize(rectBar, wxSIZE_NO_ADJUSTMENTS);
}
RefreshScrollbars();
#endif // wxUSE_SCROLLBAR
}
void wxWindow::SetScrollbar(int orient,
int pos,
int pageSize,
int range,
bool refresh)
{
#if wxUSE_SCROLLBAR
wxASSERT_MSG( pageSize <= range,
wxT("page size can't be greater than range") );
bool hasClientSizeChanged = false;
wxScrollBar *scrollbar = GetScrollbar(orient);
if ( range && (pageSize < range) )
{
if ( !scrollbar )
{
// create it
#if wxUSE_TWO_WINDOWS
SetInsertIntoMain( true );
#endif
scrollbar = new wxWindowScrollBar(this, wxID_ANY,
wxDefaultPosition, wxDefaultSize,
orient & wxVERTICAL ? wxSB_VERTICAL
: wxSB_HORIZONTAL);
#if wxUSE_TWO_WINDOWS
SetInsertIntoMain( false );
#endif
if ( orient & wxVERTICAL )
m_scrollbarVert = scrollbar;
else
m_scrollbarHorz = scrollbar;
// the client area diminished as we created a scrollbar
hasClientSizeChanged = true;
PositionScrollbars();
}
else if ( GetWindowStyle() & wxALWAYS_SHOW_SB )
{
// we might have disabled it before
scrollbar->Enable();
}
scrollbar->SetScrollbar(pos, pageSize, range, pageSize, refresh);
}
else // no range means no scrollbar
{
if ( scrollbar )
{
// wxALWAYS_SHOW_SB only applies to the vertical scrollbar
if ( (orient & wxVERTICAL) && (GetWindowStyle() & wxALWAYS_SHOW_SB) )
{
// just disable the scrollbar
scrollbar->SetScrollbar(pos, pageSize, range, pageSize, refresh);
scrollbar->Disable();
}
else // really remove the scrollbar
{
delete scrollbar;
if ( orient & wxVERTICAL )
m_scrollbarVert = NULL;
else
m_scrollbarHorz = NULL;
// the client area increased as we removed a scrollbar
hasClientSizeChanged = true;
// the size of the remaining scrollbar must be adjusted
if ( m_scrollbarHorz || m_scrollbarVert )
{
PositionScrollbars();
}
}
}
}
// give the window a chance to relayout
if ( hasClientSizeChanged )
{
#if wxUSE_TWO_WINDOWS
wxWindowNative::SetSize( GetSize() );
#else
wxSizeEvent event(GetSize());
(void)GetEventHandler()->ProcessEvent(event);
#endif
}
#else
wxUnusedVar(orient);
wxUnusedVar(pos);
wxUnusedVar(pageSize);
wxUnusedVar(range);
wxUnusedVar(refresh);
#endif // wxUSE_SCROLLBAR
}
void wxWindow::SetScrollPos(int orient, int pos, bool WXUNUSED(refresh))
{
#if wxUSE_SCROLLBAR
wxScrollBar *scrollbar = GetScrollbar(orient);
if (scrollbar)
scrollbar->SetThumbPosition(pos);
// VZ: I think we can safely ignore this as we always refresh it
// automatically whenever the value chanegs
#if 0
if ( refresh )
Refresh();
#endif
#else
wxUnusedVar(orient);
wxUnusedVar(pos);
#endif // wxUSE_SCROLLBAR
}
int wxWindow::GetScrollPos(int orient) const
{
#if wxUSE_SCROLLBAR
wxScrollBar *scrollbar = GetScrollbar(orient);
return scrollbar ? scrollbar->GetThumbPosition() : 0;
#else
wxUnusedVar(orient);
return 0;
#endif // wxUSE_SCROLLBAR
}
int wxWindow::GetScrollThumb(int orient) const
{
#if wxUSE_SCROLLBAR
wxScrollBar *scrollbar = GetScrollbar(orient);
return scrollbar ? scrollbar->GetThumbSize() : 0;
#else
wxUnusedVar(orient);
return 0;
#endif // wxUSE_SCROLLBAR
}
int wxWindow::GetScrollRange(int orient) const
{
#if wxUSE_SCROLLBAR
wxScrollBar *scrollbar = GetScrollbar(orient);
return scrollbar ? scrollbar->GetRange() : 0;
#else
wxUnusedVar(orient);
return 0;
#endif // wxUSE_SCROLLBAR
}
void wxWindow::ScrollWindow(int dx, int dy, const wxRect *rect)
{
// use native scrolling when available and do it in generic way
// otherwise:
#ifdef __WXX11__
wxWindowNative::ScrollWindow(dx, dy, rect);
#else // !wxX11
// before scrolling it, ensure that we don't have any unpainted areas
Update();
wxRect r;
if ( dx )
{
r = ScrollNoRefresh(dx, 0, rect);
Refresh(true /* erase bkgnd */, &r);
}
if ( dy )
{
r = ScrollNoRefresh(0, dy, rect);
Refresh(true /* erase bkgnd */, &r);
}
// scroll children accordingly:
wxPoint offset(dx, dy);
for (wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
node; node = node->GetNext())
{
wxWindow *child = node->GetData();
#if wxUSE_SCROLLBAR
if ( child == m_scrollbarVert || child == m_scrollbarHorz )
continue;
#endif // wxUSE_SCROLLBAR
// VS: Scrolling children has non-trivial semantics. If rect=NULL then
// it is easy: we scroll all children. Otherwise it gets
// complicated:
// 1. if scrolling in one direction only, scroll only
// those children that intersect shaft defined by the rectangle
// and scrolling direction
// 2. if scrolling in both axes, scroll all children
bool shouldMove = false;
if ( rect && (dx * dy == 0 /* moving in only one of x, y axis */) )
{
wxRect childRect = child->GetRect();
if ( dx == 0 && (childRect.GetLeft() <= rect->GetRight() ||
childRect.GetRight() >= rect->GetLeft()) )
{
shouldMove = true;
}
else if ( dy == 0 && (childRect.GetTop() <= rect->GetBottom() ||
childRect.GetBottom() >= rect->GetTop()) )
{
shouldMove = true;
}
// else: child outside of scrolling shaft, don't move
}
else // scrolling in both axes or rect=NULL
{
shouldMove = true;
}
if ( shouldMove )
child->Move(child->GetPosition() + offset, wxSIZE_ALLOW_MINUS_ONE);
}
#endif // wxX11/!wxX11
}
wxRect wxWindow::ScrollNoRefresh(int dx, int dy, const wxRect *rectTotal)
{
wxASSERT_MSG( !dx || !dy, wxT("can't be used for diag scrolling") );
// the rect to refresh (which we will calculate)
wxRect rect;
if ( !dx && !dy )
{
// nothing to do
return rect;
}
// calculate the part of the window which we can just redraw in the new
// location
wxSize sizeTotal = rectTotal ? rectTotal->GetSize() : GetClientSize();
wxLogTrace(wxT("scroll"), wxT("rect is %dx%d, scroll by %d, %d"),
sizeTotal.x, sizeTotal.y, dx, dy);
// the initial and end point of the region we move in client coords
wxPoint ptSource, ptDest;
if ( rectTotal )
{
ptSource = rectTotal->GetPosition();
ptDest = rectTotal->GetPosition();
}
// the size of this region
wxSize size;
size.x = sizeTotal.x - abs(dx);
size.y = sizeTotal.y - abs(dy);
if ( size.x <= 0 || size.y <= 0 )
{
// just redraw everything as nothing of the displayed image will stay
wxLogTrace(wxT("scroll"), wxT("refreshing everything"));
rect = rectTotal ? *rectTotal : wxRect(0, 0, sizeTotal.x, sizeTotal.y);
}
else // move the part which doesn't change to the new location
{
// note that when we scroll the canvas in some direction we move the
// block which doesn't need to be refreshed in the opposite direction
if ( dx < 0 )
{
// scroll to the right, move to the left
ptSource.x -= dx;
}
else
{
// scroll to the left, move to the right
ptDest.x += dx;
}
if ( dy < 0 )
{
// scroll down, move up
ptSource.y -= dy;
}
else
{
// scroll up, move down
ptDest.y += dy;
}
#if wxUSE_CARET
// we need to hide the caret before moving or it will erase itself at
// the wrong (old) location
wxCaret *caret = GetCaret();
if ( caret )
caret->Hide();
#endif // wxUSE_CARET
// do move
wxClientDC dc(this);
wxBitmap bmp(size.x, size.y);
wxMemoryDC dcMem;
dcMem.SelectObject(bmp);
dcMem.Blit(wxPoint(0,0), size, &dc, ptSource
#if defined(__WXGTK__) && !defined(wxHAS_WORKING_GTK_DC_BLIT)
+ GetClientAreaOrigin()
#endif // broken wxGTK wxDC::Blit
);
dc.Blit(ptDest, size, &dcMem, wxPoint(0,0));
wxLogTrace(wxT("scroll"),
wxT("Blit: (%d, %d) of size %dx%d -> (%d, %d)"),
ptSource.x, ptSource.y,
size.x, size.y,
ptDest.x, ptDest.y);
// and now repaint the uncovered area
// FIXME: We repaint the intersection of these rectangles twice - is
// it bad? I don't think so as it is rare to scroll the window
// diagonally anyhow and so adding extra logic to compute
// rectangle intersection is probably not worth the effort
rect.x = ptSource.x;
rect.y = ptSource.y;
if ( dx )
{
if ( dx < 0 )
{
// refresh the area along the right border
rect.x += size.x + dx;
rect.width = -dx;
}
else
{
// refresh the area along the left border
rect.width = dx;
}
rect.height = sizeTotal.y;
wxLogTrace(wxT("scroll"), wxT("refreshing (%d, %d)-(%d, %d)"),
rect.x, rect.y,
rect.GetRight() + 1, rect.GetBottom() + 1);
}
if ( dy )
{
if ( dy < 0 )
{
// refresh the area along the bottom border
rect.y += size.y + dy;
rect.height = -dy;
}
else
{
// refresh the area along the top border
rect.height = dy;
}
rect.width = sizeTotal.x;
wxLogTrace(wxT("scroll"), wxT("refreshing (%d, %d)-(%d, %d)"),
rect.x, rect.y,
rect.GetRight() + 1, rect.GetBottom() + 1);
}
#if wxUSE_CARET
if ( caret )
caret->Show();
#endif // wxUSE_CARET
}
return rect;
}
// ----------------------------------------------------------------------------
// accelerators and menu hot keys
// ----------------------------------------------------------------------------
#if wxUSE_MENUS
// the last window over which Alt was pressed (used by OnKeyUp)
wxWindow *wxWindow::ms_winLastAltPress = NULL;
#endif // wxUSE_MENUS
#if wxUSE_ACCEL || wxUSE_MENUS
void wxWindow::OnKeyDown(wxKeyEvent& event)
{
#if wxUSE_MENUS
int key = event.GetKeyCode();
if ( !event.ControlDown() && (key == WXK_ALT || key == WXK_F10) )
{
ms_winLastAltPress = this;
// it can't be an accel anyhow
return;
}
ms_winLastAltPress = NULL;
#endif // wxUSE_MENUS
#if wxUSE_ACCEL
for ( wxWindow *win = this; win; win = win->GetParent() )
{
int command = win->GetAcceleratorTable()->GetCommand(event);
if ( command != -1 )
{
wxCommandEvent eventCmd(wxEVT_MENU, command);
if ( win->GetEventHandler()->ProcessEvent(eventCmd) )
{
// skip "event.Skip()" below
return;
}
}
if ( win->IsTopLevel() )
{
// try the frame menu bar
#if wxUSE_MENUS
wxFrame *frame = wxDynamicCast(win, wxFrame);
if ( frame )
{
wxMenuBar *menubar = frame->GetMenuBar();
if ( menubar && menubar->ProcessAccelEvent(event) )
{
// skip "event.Skip()" below
return;
}
}
#endif // wxUSE_MENUS
#if wxUSE_BUTTON
// if it wasn't in a menu, try to find a button
if ( command != -1 )
{
wxWindow* child = win->FindWindow(command);
if ( child && wxDynamicCast(child, wxButton) )
{
wxCommandEvent eventCmd(wxEVT_BUTTON, command);
eventCmd.SetEventObject(child);
if ( child->GetEventHandler()->ProcessEvent(eventCmd) )
{
// skip "event.Skip()" below
return;
}
}
}
#endif // wxUSE_BUTTON
// don't propagate accels from the child frame to the parent one
break;
}
}
#endif // wxUSE_ACCEL
event.Skip();
}
#endif // wxUSE_ACCEL
#if wxUSE_MENUS
wxMenuBar *wxWindow::GetParentFrameMenuBar() const
{
for ( const wxWindow *win = this; win; win = win->GetParent() )
{
if ( win->IsTopLevel() )
{
wxFrame *frame = wxDynamicCast(win, wxFrame);
if ( frame )
{
return frame->GetMenuBar();
}
// don't look further - we don't want to return the menubar of the
// parent frame
break;
}
}
return NULL;
}
void wxWindow::OnChar(wxKeyEvent& event)
{
if ( event.AltDown() && !event.ControlDown() )
{
int key = event.GetKeyCode();
wxMenuBar *menubar = GetParentFrameMenuBar();
if ( menubar )
{
int item = menubar->FindNextItemForAccel(-1, key);
if ( item != -1 )
{
menubar->PopupMenu((size_t)item);
// skip "event.Skip()" below
return;
}
}
}
// if Return was pressed, see if there's a default button to activate
if ( !event.HasModifiers() && event.GetKeyCode() == WXK_RETURN )
{
wxTopLevelWindow *
tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
if ( tlw )
{
wxButton *btn = wxDynamicCast(tlw->GetDefaultItem(), wxButton);
if ( btn )
{
wxCommandEvent evt(wxEVT_BUTTON, btn->GetId());
evt.SetEventObject(btn);
btn->Command(evt);
return;
}
}
}
event.Skip();
}
void wxWindow::OnKeyUp(wxKeyEvent& event)
{
int key = event.GetKeyCode();
if ( !event.HasModifiers() && (key == WXK_ALT || key == WXK_F10) )
{
// only process Alt release specially if there were no other key
// presses since Alt had been pressed and if both events happened in
// the same window
if ( ms_winLastAltPress == this )
{
wxMenuBar *menubar = GetParentFrameMenuBar();
if ( menubar && this != menubar )
{
menubar->SelectMenu(0);
}
}
}
else
{
event.Skip();
}
// in any case reset it
ms_winLastAltPress = NULL;
}
#endif // wxUSE_MENUS
// ----------------------------------------------------------------------------
// MSW-specific section
// ----------------------------------------------------------------------------
#ifdef __WXMSW__
#include "wx/msw/private.h"
WXLRESULT wxWindow::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
{
if ( message == WM_NCHITTEST )
{
// the windows which contain the other windows should let the mouse
// events through, otherwise a window inside a static box would
// never get any events at all
if ( IsStaticBox() )
{
return HTTRANSPARENT;
}
}
return wxWindowNative::MSWWindowProc(message, wParam, lParam);
}
#endif // __WXMSW__