X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/8a6a997fb0989a09a410eaa7962a96145c0a434e..fcdd53359135f790b85728c4254b97095a56dad8:/src/common/wincmn.cpp diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp index dba13d8542..a519ffd7f0 100644 --- a/src/common/wincmn.cpp +++ b/src/common/wincmn.cpp @@ -44,6 +44,7 @@ #include "wx/scrolbar.h" #include "wx/layout.h" #include "wx/sizer.h" + #include "wx/menu.h" #endif //WX_PRECOMP #if wxUSE_DRAG_AND_DROP @@ -85,6 +86,11 @@ // Windows List WXDLLIMPEXP_DATA_CORE(wxWindowList) wxTopLevelWindows; +// globals +#if wxUSE_MENUS +wxMenu *wxCurrentPopupMenu = NULL; +#endif // wxUSE_MENUS + // ---------------------------------------------------------------------------- // static data // ---------------------------------------------------------------------------- @@ -167,7 +173,6 @@ wxWindowBase::wxWindowBase() m_windowSizer = (wxSizer *) NULL; m_containingSizer = (wxSizer *) NULL; m_autoLayout = false; - m_freeId = false; #if wxUSE_DRAG_AND_DROP m_dropTarget = (wxDropTarget *)NULL; @@ -206,6 +211,8 @@ wxWindowBase::wxWindowBase() // VZ: this one shouldn't exist... m_isBeingDeleted = false; + + m_freezeCount = 0; } // common part of window creation process @@ -239,9 +246,6 @@ bool wxWindowBase::CreateBase(wxWindowBase *parent, if ( id == wxID_ANY ) { m_windowId = NewControlId(); - - // remember to call ReleaseControlId() when this window is destroyed - m_freeId = true; } else // valid id specified { @@ -302,10 +306,6 @@ wxWindowBase::~wxWindowBase() { wxASSERT_MSG( GetCapture() != this, wxT("attempt to destroy window with mouse capture") ); - // mark the id as unused if we allocated it for this control - if ( m_freeId ) - ReleaseControlId(m_windowId); - // FIXME if these 2 cases result from programming errors in the user code // we should probably assert here instead of silently fixing them @@ -317,6 +317,13 @@ wxWindowBase::~wxWindowBase() // we weren't a dialog class wxTopLevelWindows.DeleteObject((wxWindow*)this); +#if wxUSE_MENUS + // The associated popup menu can still be alive, disassociate from it in + // this case + if ( wxCurrentPopupMenu && wxCurrentPopupMenu->GetInvokingWindow() == this ) + wxCurrentPopupMenu->SetInvokingWindow(NULL); +#endif // wxUSE_MENUS + wxASSERT_MSG( GetChildren().GetCount() == 0, wxT("children not destroyed") ); // notify the parent about this window destruction @@ -362,6 +369,20 @@ wxWindowBase::~wxWindowBase() #if wxUSE_ACCESSIBILITY delete m_accessible; #endif + +#if wxUSE_HELP + // NB: this has to be called unconditionally, because we don't know + // whether this window has associated help text or not + wxHelpProvider *helpProvider = wxHelpProvider::Get(); + if ( helpProvider ) + helpProvider->RemoveHelp(this); +#endif +} + +bool wxWindowBase::IsBeingDeleted() const +{ + return m_isBeingDeleted || + (!IsTopLevel() && m_parent && m_parent->IsBeingDeleted()); } void wxWindowBase::SendDestroyEvent() @@ -387,7 +408,7 @@ bool wxWindowBase::Close(bool force) // return false if window wasn't closed because the application vetoed the // close event - return GetEventHandler()->ProcessEvent(event) && !event.GetVeto(); + return HandleWindowEvent(event) && !event.GetVeto(); } bool wxWindowBase::DestroyChildren() @@ -603,9 +624,10 @@ wxSize wxWindowBase::DoGetBestSize() const // helper of GetWindowBorderSize(): as many ports don't implement support for // wxSYS_BORDER/EDGE_X/Y metrics in their wxSystemSettings, use hard coded // fallbacks in this case -static int wxGetMetricOrDefault(wxSystemMetric what) +static int wxGetMetricOrDefault(wxSystemMetric what, const wxWindowBase* win) { - int rc = wxSystemSettings::GetMetric(what); + int rc = wxSystemSettings::GetMetric( + what, static_cast(const_cast(win))); if ( rc == -1 ) { switch ( what ) @@ -643,23 +665,23 @@ wxSize wxWindowBase::GetWindowBorderSize() const case wxBORDER_SIMPLE: case wxBORDER_STATIC: - size.x = wxGetMetricOrDefault(wxSYS_BORDER_X); - size.y = wxGetMetricOrDefault(wxSYS_BORDER_Y); + size.x = wxGetMetricOrDefault(wxSYS_BORDER_X, this); + size.y = wxGetMetricOrDefault(wxSYS_BORDER_Y, this); break; case wxBORDER_SUNKEN: case wxBORDER_RAISED: - size.x = wxMax(wxGetMetricOrDefault(wxSYS_EDGE_X), - wxGetMetricOrDefault(wxSYS_BORDER_X)); - size.y = wxMax(wxGetMetricOrDefault(wxSYS_EDGE_Y), - wxGetMetricOrDefault(wxSYS_BORDER_Y)); + size.x = wxMax(wxGetMetricOrDefault(wxSYS_EDGE_X, this), + wxGetMetricOrDefault(wxSYS_BORDER_X, this)); + size.y = wxMax(wxGetMetricOrDefault(wxSYS_EDGE_Y, this), + wxGetMetricOrDefault(wxSYS_BORDER_Y, this)); break; case wxBORDER_DOUBLE: - size.x = wxGetMetricOrDefault(wxSYS_EDGE_X) + - wxGetMetricOrDefault(wxSYS_BORDER_X); - size.y = wxGetMetricOrDefault(wxSYS_EDGE_Y) + - wxGetMetricOrDefault(wxSYS_BORDER_Y); + size.x = wxGetMetricOrDefault(wxSYS_EDGE_X, this) + + wxGetMetricOrDefault(wxSYS_BORDER_X, this); + size.y = wxGetMetricOrDefault(wxSYS_EDGE_Y, this) + + wxGetMetricOrDefault(wxSYS_BORDER_Y, this); break; default: @@ -706,6 +728,22 @@ wxPoint wxWindowBase::GetClientAreaOrigin() const return wxPoint(0,0); } +wxSize wxWindowBase::ClientToWindowSize(const wxSize& size) const +{ + const wxSize diff(GetSize() - GetClientSize()); + + return wxSize(size.x == -1 ? -1 : size.x + diff.x, + size.y == -1 ? -1 : size.y + diff.y); +} + +wxSize wxWindowBase::WindowToClientSize(const wxSize& size) const +{ + const wxSize diff(GetSize() - GetClientSize()); + + return wxSize(size.x == -1 ? -1 : size.x - diff.x, + size.y == -1 ? -1 : size.y - diff.y); +} + void wxWindowBase::SetWindowVariant( wxWindowVariant variant ) { if ( m_windowVariant != variant ) @@ -810,6 +848,23 @@ void wxWindowBase::DoGetScreenPosition(int *x, int *y) const ClientToScreen(x, y); } +void wxWindowBase::SendSizeEvent(int flags) +{ + wxSizeEvent event(GetSize(), GetId()); + event.SetEventObject(this); + if ( flags & wxSEND_EVENT_POST ) + wxPostEvent(this, event); + else + HandleWindowEvent(event); +} + +void wxWindowBase::SendSizeEventToParent(int flags) +{ + wxWindow * const parent = GetParent(); + if ( parent && !parent->IsBeingDeleted() ) + parent->SendSizeEvent(flags); +} + // ---------------------------------------------------------------------------- // show/hide/enable/disable the window // ---------------------------------------------------------------------------- @@ -897,6 +952,52 @@ bool wxWindowBase::IsTopLevel() const return false; } +// ---------------------------------------------------------------------------- +// Freeze/Thaw +// ---------------------------------------------------------------------------- + +void wxWindowBase::Freeze() +{ + if ( !m_freezeCount++ ) + { + // physically freeze this window: + DoFreeze(); + + // and recursively freeze all children: + for ( wxWindowList::iterator i = GetChildren().begin(); + i != GetChildren().end(); ++i ) + { + wxWindow *child = *i; + if ( child->IsTopLevel() ) + continue; + + child->Freeze(); + } + } +} + +void wxWindowBase::Thaw() +{ + wxASSERT_MSG( m_freezeCount, "Thaw() without matching Freeze()" ); + + if ( !--m_freezeCount ) + { + // recursively thaw all children: + for ( wxWindowList::iterator i = GetChildren().begin(); + i != GetChildren().end(); ++i ) + { + wxWindow *child = *i; + if ( child->IsTopLevel() ) + continue; + + child->Thaw(); + } + + // physically thaw this window: + DoThaw(); + } +} + // ---------------------------------------------------------------------------- // reparenting the window // ---------------------------------------------------------------------------- @@ -912,12 +1013,25 @@ void wxWindowBase::AddChild(wxWindowBase *child) GetChildren().Append((wxWindow*)child); child->SetParent(this); + + // adding a child while frozen will assert when thawed, so freeze it as if + // it had been already present when we were frozen + if ( IsFrozen() && !child->IsTopLevel() ) + child->Freeze(); } void wxWindowBase::RemoveChild(wxWindowBase *child) { wxCHECK_RET( child, wxT("can't remove a NULL child") ); + // removing a child while frozen may result in permanently frozen window + // if used e.g. from Reparent(), so thaw it + // + // NB: IsTopLevel() doesn't return true any more when a TLW child is being + // removed from its ~wxWindowBase, so check for IsBeingDeleted() too + if ( IsFrozen() && !child->IsBeingDeleted() && !child->IsTopLevel() ) + child->Thaw(); + GetChildren().DeleteObject((wxWindow *)child); child->SetParent(NULL); } @@ -1312,7 +1426,7 @@ void wxWindowBase::ClearBackground() // wxGTK uses its own version, no need to add never used code #ifndef __WXGTK__ wxClientDC dc((wxWindow *)this); - wxBrush brush(GetBackgroundColour(), wxSOLID); + wxBrush brush(GetBackgroundColour(), wxBRUSHSTYLE_SOLID); dc.SetBackground(brush); dc.Clear(); #endif // __WXGTK__ @@ -1608,6 +1722,7 @@ void wxWindowBase::SetHelpText(const wxString& text) } } +#if WXWIN_COMPATIBILITY_2_8 // associate this help text with all windows with the same id as this // one void wxWindowBase::SetHelpTextForId(const wxString& text) @@ -1618,6 +1733,7 @@ void wxWindowBase::SetHelpTextForId(const wxString& text) helpProvider->AddHelp(GetId(), text); } } +#endif // WXWIN_COMPATIBILITY_2_8 // get the help string associated with this window (may be empty) // default implementation forwards calls to the help provider @@ -1641,7 +1757,29 @@ void wxWindowBase::OnHelp(wxHelpEvent& event) wxHelpProvider *helpProvider = wxHelpProvider::Get(); if ( helpProvider ) { - if ( helpProvider->ShowHelpAtPoint(this, event.GetPosition(), event.GetOrigin()) ) + wxPoint pos = event.GetPosition(); + const wxHelpEvent::Origin origin = event.GetOrigin(); + if ( origin == wxHelpEvent::Origin_Keyboard ) + { + // if the help event was generated from keyboard it shouldn't + // appear at the mouse position (which is still the only position + // associated with help event) if the mouse is far away, although + // we still do use the mouse position if it's over the window + // because we suppose the user looks approximately at the mouse + // already and so it would be more convenient than showing tooltip + // at some arbitrary position which can be quite far from it + const wxRect rectClient = GetClientRect(); + if ( !rectClient.Contains(ScreenToClient(pos)) ) + { + // position help slightly under and to the right of this window + pos = ClientToScreen(wxPoint( + 2*GetCharWidth(), + rectClient.height + GetCharHeight() + )); + } + } + + if ( helpProvider->ShowHelpAtPoint(this, pos, origin) ) { // skip the event.Skip() below return; @@ -2245,6 +2383,17 @@ void wxWindowBase::OnInitDialog( wxInitDialogEvent &WXUNUSED(event) ) #if wxUSE_MENUS +bool wxWindowBase::PopupMenu(wxMenu *menu, int x, int y) +{ + wxCHECK_MSG( menu, false, "can't popup NULL menu" ); + + wxCurrentPopupMenu = menu; + const bool rc = DoPopupMenu(menu, x, y); + wxCurrentPopupMenu = NULL; + + return rc; +} + // this is used to pass the id of the selected item from the menu event handler // to the main function itself // @@ -2258,6 +2407,11 @@ void wxWindowBase::InternalOnPopupMenu(wxCommandEvent& event) gs_popupMenuSelection = event.GetId(); } +void wxWindowBase::InternalOnPopupMenuUpdate(wxUpdateUIEvent& WXUNUSED(event)) +{ + // nothing to do but do not skip it +} + int wxWindowBase::DoGetPopupMenuSelectionFromUser(wxMenu& menu, int x, int y) { @@ -2268,8 +2422,24 @@ wxWindowBase::DoGetPopupMenuSelectionFromUser(wxMenu& menu, int x, int y) NULL, this); + // it is common to construct the menu passed to this function dynamically + // using some fixed range of ids which could clash with the ids used + // elsewhere in the program, which could result in some menu items being + // unintentionally disabled or otherwise modified by update UI handlers + // elsewhere in the program code and this is difficult to avoid in the + // program itself, so instead we just temporarily suspend UI updating while + // this menu is shown + Connect(wxEVT_UPDATE_UI, + wxUpdateUIEventHandler(wxWindowBase::InternalOnPopupMenuUpdate), + NULL, + this); + PopupMenu(&menu, x, y); + Disconnect(wxEVT_UPDATE_UI, + wxUpdateUIEventHandler(wxWindowBase::InternalOnPopupMenuUpdate), + NULL, + this); Disconnect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(wxWindowBase::InternalOnPopupMenu), NULL, @@ -2289,7 +2459,7 @@ static void DrawBorder(wxWindowBase *win, const wxRect& rect, bool fill = false) { wxClientDC dc((wxWindow *)win); dc.SetPen(*wxRED_PEN); - dc.SetBrush(fill ? wxBrush(*wxRED, wxCROSSDIAG_HATCH): *wxTRANSPARENT_BRUSH); + dc.SetBrush(fill ? wxBrush(*wxRED, wxBRUSHSTYLE_CROSSDIAG_HATCH) : *wxTRANSPARENT_BRUSH); dc.DrawRectangle(rect.Deflate(1, 1)); } @@ -2468,7 +2638,7 @@ bool wxWindowBase::ms_winCaptureChanging = false; void wxWindowBase::CaptureMouse() { - wxLogTrace(_T("mousecapture"), _T("CaptureMouse(%p)"), wx_static_cast(void*, this)); + wxLogTrace(_T("mousecapture"), _T("CaptureMouse(%p)"), static_cast(this)); wxASSERT_MSG( !ms_winCaptureChanging, _T("recursive CaptureMouse call?") ); @@ -2495,11 +2665,14 @@ void wxWindowBase::CaptureMouse() void wxWindowBase::ReleaseMouse() { - wxLogTrace(_T("mousecapture"), _T("ReleaseMouse(%p)"), wx_static_cast(void*, this)); + wxLogTrace(_T("mousecapture"), _T("ReleaseMouse(%p)"), static_cast(this)); wxASSERT_MSG( !ms_winCaptureChanging, _T("recursive ReleaseMouse call?") ); - wxASSERT_MSG( GetCapture() == this, wxT("attempt to release mouse, but this window hasn't captured it") ); + wxASSERT_MSG( GetCapture() == this, + "attempt to release mouse, but this window hasn't captured it" ); + wxASSERT_MSG( ms_winCaptureCurrent == this, + "attempt to release mouse, but this window hasn't captured it" ); ms_winCaptureChanging = true; @@ -2521,7 +2694,7 @@ void wxWindowBase::ReleaseMouse() wxLogTrace(_T("mousecapture"), (const wxChar *) _T("After ReleaseMouse() mouse is captured by %p"), - wx_static_cast(void*, GetCapture())); + static_cast(GetCapture())); } static void DoNotifyWindowAboutCaptureLost(wxWindow *win) @@ -2596,8 +2769,8 @@ bool wxWindowBase::TryValidator(wxEvent& wxVALIDATOR_PARAM(event)) // is receiving the event if ( event.GetEventObject() == this ) { - wxValidator *validator = GetValidator(); - if ( validator && validator->ProcessEvent(event) ) + wxValidator * const validator = GetValidator(); + if ( validator && validator->ProcessEventHere(event) ) { return true; } @@ -2666,12 +2839,33 @@ bool wxWindowBase::DoNavigateIn(int flags) return false; #else // !wxHAS_NATIVE_TAB_TRAVERSAL wxNavigationKeyEvent eventNav; + wxWindow *focused = FindFocus(); + eventNav.SetCurrentFocus(focused); + eventNav.SetEventObject(focused); eventNav.SetFlags(flags); - eventNav.SetEventObject(FindFocus()); return GetEventHandler()->ProcessEvent(eventNav); #endif // wxHAS_NATIVE_TAB_TRAVERSAL/!wxHAS_NATIVE_TAB_TRAVERSAL } +bool wxWindowBase::HandleAsNavigationKey(const wxKeyEvent& event) +{ + if ( event.GetKeyCode() != WXK_TAB ) + return false; + + int flags = wxNavigationKeyEvent::FromTab; + + if ( event.ShiftDown() ) + flags |= wxNavigationKeyEvent::IsBackward; + else + flags |= wxNavigationKeyEvent::IsForward; + + if ( event.ControlDown() ) + flags |= wxNavigationKeyEvent::WinChange; + + Navigate(flags); + return true; +} + void wxWindowBase::DoMoveInTabOrder(wxWindow *win, WindowOrder move) { // check that we're not a top level window @@ -2717,6 +2911,68 @@ void wxWindowBase::DoMoveInTabOrder(wxWindow *win, WindowOrder move) return win ? win->GetMainWindowOfCompositeControl() : NULL; } +bool wxWindowBase::HasFocus() const +{ + wxWindowBase *win = DoFindFocus(); + return win == this || + win == wxConstCast(this, wxWindowBase)->GetMainWindowOfCompositeControl(); +} + +// ---------------------------------------------------------------------------- +// drag and drop +// ---------------------------------------------------------------------------- + +#if wxUSE_DRAG_AND_DROP && !defined(__WXMSW__) + +namespace +{ + +class DragAcceptFilesTarget : public wxFileDropTarget +{ +public: + DragAcceptFilesTarget(wxWindowBase *win) : m_win(win) {} + + virtual bool OnDropFiles(wxCoord x, wxCoord y, + const wxArrayString& filenames) + { + wxDropFilesEvent event(wxEVT_DROP_FILES, + filenames.size(), + wxCArrayString(filenames).Release()); + event.SetEventObject(m_win); + event.m_pos.x = x; + event.m_pos.y = y; + + return m_win->HandleWindowEvent(event); + } + +private: + wxWindowBase * const m_win; + + DECLARE_NO_COPY_CLASS(DragAcceptFilesTarget) +}; + + +} // anonymous namespace + +// Generic version of DragAcceptFiles(). It works by installing a simple +// wxFileDropTarget-to-EVT_DROP_FILES adaptor and therefore cannot be used +// together with explicit SetDropTarget() calls. +void wxWindowBase::DragAcceptFiles(bool accept) +{ + if ( accept ) + { + wxASSERT_MSG( !GetDropTarget(), + "cannot use DragAcceptFiles() and SetDropTarget() together" ); + SetDropTarget(new DragAcceptFilesTarget(this)); + } + else + { + SetDropTarget(NULL); + } +} + +#endif // wxUSE_DRAG_AND_DROP && !defined(__WXMSW__) + // ---------------------------------------------------------------------------- // global functions // ---------------------------------------------------------------------------- @@ -3171,125 +3427,4 @@ wxWindowBase::AdjustForLayoutDirection(wxCoord x, return x; } -// ---------------------------------------------------------------------------- -// Window (and menu items) identifiers management -// ---------------------------------------------------------------------------- - -namespace -{ - -// this array contains, in packed form, the "in use" flags for the entire -// auto-generated ids range: N-th element of the array contains the flags for -// ids in [wxID_AUTO_LOWEST + 8*N, wxID_AUTO_LOWEST + 8*N + 7] range -// -// initially no ids are in use and we allocate them consecutively, but after we -// exhaust the entire range, we wrap around and reuse the ids freed in the -// meanwhile -wxByte gs_autoIdsInUse[(wxID_AUTO_HIGHEST - wxID_AUTO_LOWEST + 1)/8 + 1] = { 0 }; -// this is an optimization used until we wrap around wxID_AUTO_HIGHEST: if this -// value is < wxID_AUTO_HIGHEST we know that we haven't wrapped yet and so can -// allocate the ids simply by incrementing it -static wxWindowID gs_nextControlId = wxID_AUTO_LOWEST; - -void MarkAutoIdUsed(wxWindowID id) -{ - id -= wxID_AUTO_LOWEST; - - const int theByte = id / 8; - const int theBit = id % 8; - - gs_autoIdsInUse[theByte] |= 1 << theBit; -} - -void FreeAutoId(wxWindowID id) -{ - id -= wxID_AUTO_LOWEST; - - const int theByte = id / 8; - const int theBit = id % 8; - - gs_autoIdsInUse[theByte] &= ~(1 << theBit); -} - -bool IsAutoIdInUse(wxWindowID id) -{ - id -= wxID_AUTO_LOWEST; - - const int theByte = id / 8; - const int theBit = id % 8; - - return (gs_autoIdsInUse[theByte] & (1 << theBit)) != 0; -} - -} // anonymous namespace - - -/* static */ -bool wxWindowBase::IsAutoGeneratedId(wxWindowID id) -{ - if ( id < wxID_AUTO_LOWEST || id > wxID_AUTO_HIGHEST ) - return false; - - // we shouldn't have any stray ids in this range - wxASSERT_MSG( IsAutoIdInUse(id), "unused automatically generated id?" ); - - return true; -} - -wxWindowID wxWindowBase::NewControlId(int count) -{ - wxASSERT_MSG( count > 0, "can't allocate less than 1 id" ); - - if ( gs_nextControlId + count - 1 <= wxID_AUTO_HIGHEST ) - { - // we haven't wrapped yet, so we can just grab the next count ids - wxWindowID id = gs_nextControlId; - - while ( count-- ) - MarkAutoIdUsed(gs_nextControlId++); - - return id; - } - else // we've already wrapped or are now going to - { - // brute-force search for the id values - - // number of consecutive free ids found so far - int found = 0; - - for ( wxWindowID id = wxID_AUTO_LOWEST; id <= wxID_AUTO_HIGHEST; id++ ) - { - if ( !IsAutoIdInUse(id) ) - { - // found another consecutive available id - found++; - if ( found == count ) - { - // mark all count consecutive free ids we found as being in - // use now and rewind back to the start of available range - // in the process - while ( count-- ) - MarkAutoIdUsed(id--); - - return id; - } - } - else // this id is in use - { - // reset the number of consecutive free values found - found = 0; - } - } - } - - // if we get here, there are not enough consecutive free ids - return wxID_NONE; -} - -void wxWindowBase::ReleaseControlId(wxWindowID id) -{ - wxCHECK_RET( IsAutoGeneratedId(id), "can't release non auto-generated id" ); - - FreeAutoId(id); -}