X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/b21f4960b13ad92398975de63e3a3f1de8536e52..888dde65f43d5f57e8fb2028b27191cca1741403:/src/common/wincmn.cpp diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp index 66570d7882..5c25f4f47a 100644 --- a/src/common/wincmn.cpp +++ b/src/common/wincmn.cpp @@ -78,9 +78,6 @@ #else #include #endif - extern const unsigned int gtk_major_version; - extern const unsigned int gtk_minor_version; - extern const unsigned int gtk_micro_version; #endif #include "wx/platinfo.h" @@ -92,13 +89,6 @@ WXDLLIMPEXP_DATA_CORE(wxWindowList) wxTopLevelWindows; // static data // ---------------------------------------------------------------------------- -#if defined(__WXPALMOS__) -int wxWindowBase::ms_lastControlId = 32767; -#elif defined(__WXPM__) -int wxWindowBase::ms_lastControlId = 2000; -#else -int wxWindowBase::ms_lastControlId = -200; -#endif IMPLEMENT_ABSTRACT_CLASS(wxWindowBase, wxEvtHandler) @@ -177,6 +167,7 @@ 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; @@ -245,14 +236,29 @@ bool wxWindowBase::CreateBase(wxWindowBase *parent, // ids are limited to 16 bits under MSW so if you care about portability, // it's not a good idea to use ids out of this range (and negative ids are // reserved for wxWidgets own usage) - wxASSERT_MSG( id == wxID_ANY || (id >= 0 && id < 32767), + wxASSERT_MSG( id == wxID_ANY || (id >= 0 && id < 32767) || + (id >= wxID_AUTO_LOWEST && id <= wxID_AUTO_HIGHEST), _T("invalid id value") ); // generate a new id if the user doesn't care about it - m_windowId = id == wxID_ANY ? NewControlId() : id; + if ( id == wxID_ANY ) + { + m_windowId = NewControlId(); + + // remember to call ReleaseControlId() when this window is destroyed + m_freeId = true; + } + else // valid id specified + { + m_windowId = id; + } + + // don't use SetWindowStyleFlag() here, this function should only be called + // to change the flag after creation as it tries to reflect the changes in + // flags by updating the window dynamically and we don't need this here + m_windowStyle = style; SetName(name); - SetWindowStyleFlag(style); SetParent(parent); #if wxUSE_VALIDATORS @@ -270,6 +276,28 @@ bool wxWindowBase::CreateBase(wxWindowBase *parent, return true; } +bool wxWindowBase::ToggleWindowStyle(int flag) +{ + wxASSERT_MSG( flag, _T("flags with 0 value can't be toggled") ); + + bool rc; + long style = GetWindowStyleFlag(); + if ( style & flag ) + { + style &= ~flag; + rc = false; + } + else // currently off + { + style |= flag; + rc = true; + } + + SetWindowStyleFlag(style); + + return rc; +} + // ---------------------------------------------------------------------------- // destruction // ---------------------------------------------------------------------------- @@ -279,6 +307,10 @@ 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 @@ -292,23 +324,9 @@ wxWindowBase::~wxWindowBase() wxASSERT_MSG( GetChildren().GetCount() == 0, wxT("children not destroyed") ); - // reset the top-level parent's default item if it is this widget + // notify the parent about this window destruction if ( m_parent ) - { - wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent((wxWindow*)this), - wxTopLevelWindow); - - if ( tlw && tlw->GetDefaultItem() == this ) - tlw->SetDefaultItem(NULL); - if ( tlw && tlw->GetTmpDefaultItem() == this ) - tlw->SetTmpDefaultItem(NULL); - } - - // reset the dangling pointer our parent window may keep to us - if ( m_parent ) - { m_parent->RemoveChild(this); - } #if wxUSE_CARET delete m_caret; @@ -351,6 +369,14 @@ wxWindowBase::~wxWindowBase() #endif } +void wxWindowBase::SendDestroyEvent() +{ + wxWindowDestroyEvent event; + event.SetEventObject(this); + event.SetId(GetId()); + GetEventHandler()->ProcessEvent(event); +} + bool wxWindowBase::Destroy() { delete this; @@ -463,7 +489,7 @@ wxSize wxWindowBase::DoGetBestSize() const if ( m_windowSizer ) { - best = GetWindowSizeForVirtualSize(m_windowSizer->GetMinSize()); + best = m_windowSizer->GetMinSize(); } #if wxUSE_CONSTRAINTS else if ( m_constraints ) @@ -579,6 +605,76 @@ wxSize wxWindowBase::DoGetBestSize() const return best; } +// 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) +{ + int rc = wxSystemSettings::GetMetric(what); + if ( rc == -1 ) + { + switch ( what ) + { + case wxSYS_BORDER_X: + case wxSYS_BORDER_Y: + // 2D border is by default 1 pixel wide + rc = 1; + break; + + case wxSYS_EDGE_X: + case wxSYS_EDGE_Y: + // 3D borders are by default 2 pixels + rc = 2; + break; + + default: + wxFAIL_MSG( _T("unexpected wxGetMetricOrDefault() argument") ); + rc = 0; + } + } + + return rc; +} + +wxSize wxWindowBase::GetWindowBorderSize() const +{ + wxSize size; + + switch ( GetBorder() ) + { + case wxBORDER_NONE: + // nothing to do, size is already (0, 0) + break; + + case wxBORDER_SIMPLE: + case wxBORDER_STATIC: + size.x = wxGetMetricOrDefault(wxSYS_BORDER_X); + size.y = wxGetMetricOrDefault(wxSYS_BORDER_Y); + 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)); + break; + + case wxBORDER_DOUBLE: + size.x = wxGetMetricOrDefault(wxSYS_EDGE_X) + + wxGetMetricOrDefault(wxSYS_BORDER_X); + size.y = wxGetMetricOrDefault(wxSYS_EDGE_Y) + + wxGetMetricOrDefault(wxSYS_BORDER_Y); + break; + + default: + wxFAIL_MSG(_T("Unknown border style.")); + break; + } + + // we have borders on both sides + return size*2; +} wxSize wxWindowBase::GetEffectiveMinSize() const { @@ -662,7 +758,7 @@ void wxWindowBase::DoSetWindowVariant( wxWindowVariant variant ) void wxWindowBase::DoSetSizeHints( int minW, int minH, int maxW, int maxH, - int incW, int incH ) + int WXUNUSED(incW), int WXUNUSED(incH) ) { wxCHECK_RET( (minW == wxDefaultCoord || maxW == wxDefaultCoord || minW <= maxW) && (minH == wxDefaultCoord || maxH == wxDefaultCoord || minH <= maxH), @@ -743,18 +839,55 @@ bool wxWindowBase::Show(bool show) } } -bool wxWindowBase::Enable(bool enable) +bool wxWindowBase::IsEnabled() const { - if ( enable != m_isEnabled ) - { - m_isEnabled = enable; + return IsThisEnabled() && (IsTopLevel() || !GetParent() || GetParent()->IsEnabled()); +} - return true; - } - else +void wxWindowBase::NotifyWindowOnEnableChange(bool enabled) +{ +#ifndef wxHAS_NATIVE_ENABLED_MANAGEMENT + DoEnable(enabled); +#endif // !defined(wxHAS_NATIVE_ENABLED_MANAGEMENT) + + OnEnabled(enabled); + + // If we are top-level then the logic doesn't apply - otherwise + // showing a modal dialog would result in total greying out (and ungreying + // out later) of everything which would be really ugly + if ( IsTopLevel() ) + return; + + for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); + node; + node = node->GetNext() ) { + wxWindowBase * const child = node->GetData(); + if ( !child->IsTopLevel() && child->IsThisEnabled() ) + child->NotifyWindowOnEnableChange(enabled); + } +} + +bool wxWindowBase::Enable(bool enable) +{ + if ( enable == IsThisEnabled() ) return false; + + m_isEnabled = enable; + +#ifdef wxHAS_NATIVE_ENABLED_MANAGEMENT + DoEnable(enable); +#else // !defined(wxHAS_NATIVE_ENABLED_MANAGEMENT) + wxWindowBase * const parent = GetParent(); + if( !IsTopLevel() && parent && !parent->IsEnabled() ) + { + return true; } +#endif // !defined(wxHAS_NATIVE_ENABLED_MANAGEMENT) + + NotifyWindowOnEnableChange(enable); + + return true; } bool wxWindowBase::IsShownOnScreen() const @@ -806,6 +939,8 @@ bool wxWindowBase::Reparent(wxWindowBase *newParent) return false; } + const bool oldEnabledState = IsEnabled(); + // unlink this window from the existing parent. if ( oldParent ) { @@ -826,6 +961,14 @@ bool wxWindowBase::Reparent(wxWindowBase *newParent) wxTopLevelWindows.Append((wxWindow *)this); } + // We need to notify window (and its subwindows) if by changing the parent + // we also change our enabled/disabled status. + const bool newEnabledState = IsEnabled(); + if ( newEnabledState != oldEnabledState ) + { + NotifyWindowOnEnableChange(newEnabledState); + } + return true; } @@ -908,6 +1051,11 @@ bool wxWindowBase::RemoveEventHandler(wxEvtHandler *handler) return false; } +bool wxWindowBase::HandleWindowEvent(wxEvent& event) const +{ + return GetEventHandler()->SafelyProcessEvent(event); +} + // ---------------------------------------------------------------------------- // colours, fonts &c // ---------------------------------------------------------------------------- @@ -994,8 +1142,6 @@ wxColour wxWindowBase::GetForegroundColour() const // logic is the same as above if ( !m_hasFgCol && !m_foregroundColour.Ok() ) { - wxASSERT_MSG( !m_hasFgCol, _T("we have invalid explicit fg colour?") ); - wxColour colFg = GetDefaultAttributes().colFg; if ( !colFg.Ok() ) @@ -1516,7 +1662,7 @@ void wxWindowBase::OnHelp(wxHelpEvent& event) #endif // wxUSE_HELP // ---------------------------------------------------------------------------- -// tooltipsroot.Replace("\\", "/"); +// tooltips // ---------------------------------------------------------------------------- #if wxUSE_TOOLTIPS @@ -2000,7 +2146,7 @@ void wxWindowBase::AdjustForParentClientOrigin(int& x, int& y, int sizeFlags) co } // ---------------------------------------------------------------------------- -// do Update UI processing for child controls +// Update UI processing // ---------------------------------------------------------------------------- void wxWindowBase::UpdateWindowUI(long flags) @@ -2035,23 +2181,6 @@ void wxWindowBase::DoUpdateWindowUI(wxUpdateUIEvent& event) Show(event.GetShown()); } -#if 0 -// call internal idle recursively -// may be obsolete (wait until OnIdle scheme stabilises) -void wxWindowBase::ProcessInternalIdle() -{ - OnInternalIdle(); - - wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); - while (node) - { - wxWindow *child = node->GetData(); - child->ProcessInternalIdle(); - node = node->GetNext(); - } -} -#endif - // ---------------------------------------------------------------------------- // dialog units translations // ---------------------------------------------------------------------------- @@ -2118,6 +2247,47 @@ void wxWindowBase::OnInitDialog( wxInitDialogEvent &WXUNUSED(event) ) UpdateWindowUI(wxUPDATE_UI_RECURSE); } +// ---------------------------------------------------------------------------- +// menu-related functions +// ---------------------------------------------------------------------------- + +#if wxUSE_MENUS + +// this is used to pass the id of the selected item from the menu event handler +// to the main function itself +// +// it's ok to use a global here as there can be at most one popup menu shown at +// any time +static int gs_popupMenuSelection = wxID_NONE; + +void wxWindowBase::InternalOnPopupMenu(wxCommandEvent& event) +{ + // store the id in a global variable where we'll retrieve it from later + gs_popupMenuSelection = event.GetId(); +} + +int +wxWindowBase::DoGetPopupMenuSelectionFromUser(wxMenu& menu, int x, int y) +{ + gs_popupMenuSelection = wxID_NONE; + + Connect(wxEVT_COMMAND_MENU_SELECTED, + wxCommandEventHandler(wxWindowBase::InternalOnPopupMenu), + NULL, + this); + + PopupMenu(&menu, x, y); + + Disconnect(wxEVT_COMMAND_MENU_SELECTED, + wxCommandEventHandler(wxWindowBase::InternalOnPopupMenu), + NULL, + this); + + return gs_popupMenuSelection; +} + +#endif // wxUSE_MENUS + // methods for drawing the sizers in a visible way #ifdef __WXDEBUG__ @@ -2192,45 +2362,9 @@ void wxWindowBase::OnMiddleClick( wxMouseEvent& event ) return; } #endif // __WXDEBUG__ - -#if wxUSE_MSGDLG - // don't translate these strings, they're for diagnostics purposes only - wxString msg; - msg.Printf(_T("wxWidgets Library (%s port)\n") - _T("Version %d.%d.%d%s%s, compiled at %s %s\n") - _T("Runtime version of toolkit used is %d.%d.%s\n") - _T("Copyright (c) 1995-2006 wxWidgets team"), - wxPlatformInfo::Get().GetPortIdName().c_str(), - wxMAJOR_VERSION, - wxMINOR_VERSION, - wxRELEASE_NUMBER, -#if wxUSE_UNICODE - L" (Unicode)", -#else - wxEmptyString, -#endif -#ifdef __WXDEBUG__ - _T(" Debug build"), -#else - wxEmptyString, -#endif - __TDATE__, - __TTIME__, - wxPlatformInfo::Get().GetToolkitMajorVersion(), - wxPlatformInfo::Get().GetToolkitMinorVersion(), -#ifdef __WXGTK__ - wxString::Format(_T("\nThe compile-time GTK+ version is %d.%d.%d."), GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION).c_str() -#else - wxEmptyString -#endif - ); - - wxMessageBox(msg, _T("wxWidgets information"), - wxICON_INFORMATION | wxOK, - (wxWindow *)this); + ::wxInfoMessageBox((wxWindow*)this); } else -#endif // wxUSE_MSGDLG { event.Skip(); } @@ -2275,14 +2409,14 @@ wxAccessible* wxWindowBase::CreateAccessible() #include "wx/listimpl.cpp" WX_DEFINE_LIST(wxWindowList) -#else +#else // !wxUSE_STL void wxWindowListNode::DeleteData() { delete (wxWindow *)GetData(); } -#endif +#endif // wxUSE_STL/!wxUSE_STL // ---------------------------------------------------------------------------- // borders @@ -2295,6 +2429,10 @@ wxBorder wxWindowBase::GetBorder(long flags) const { border = GetDefaultBorder(); } + else if ( border == wxBORDER_THEME ) + { + border = GetDefaultBorderForControl(); + } return border; } @@ -2400,6 +2538,10 @@ static void DoNotifyWindowAboutCaptureLost(wxWindow *win) event.SetEventObject(win); if ( !win->GetEventHandler()->ProcessEvent(event) ) { + // windows must handle this event, otherwise the app wouldn't behave + // correctly if it loses capture unexpectedly; see the discussion here: + // http://sourceforge.net/tracker/index.php?func=detail&aid=1153662&group_id=9863&atid=109863 + // http://article.gmane.org/gmane.comp.lib.wxwidgets.devel/82376 wxFAIL_MSG( _T("window that captured the mouse didn't process wxEVT_MOUSE_CAPTURE_LOST") ); } } @@ -2451,14 +2593,6 @@ bool wxWindowBase::UnregisterHotKey(int WXUNUSED(hotkeyId)) #endif // wxUSE_HOTKEY -void wxWindowBase::SendDestroyEvent() -{ - wxWindowDestroyEvent event; - event.SetEventObject(this); - event.SetId(GetId()); - GetEventHandler()->ProcessEvent(event); -} - // ---------------------------------------------------------------------------- // event processing // ---------------------------------------------------------------------------- @@ -2506,24 +2640,47 @@ bool wxWindowBase::TryParent(wxEvent& event) return wxEvtHandler::TryParent(event); } +// ---------------------------------------------------------------------------- +// window relationships +// ---------------------------------------------------------------------------- + +wxWindow *wxWindowBase::DoGetSibling(WindowOrder order) const +{ + wxCHECK_MSG( GetParent(), NULL, + _T("GetPrev/NextSibling() don't work for TLWs!") ); + + wxWindowList& siblings = GetParent()->GetChildren(); + wxWindowList::compatibility_iterator i = siblings.Find((wxWindow *)this); + wxCHECK_MSG( i, NULL, _T("window not a child of its parent?") ); + + if ( order == OrderBefore ) + i = i->GetPrevious(); + else // OrderAfter + i = i->GetNext(); + + return i ? i->GetData() : NULL; +} + // ---------------------------------------------------------------------------- // keyboard navigation // ---------------------------------------------------------------------------- -// Navigates in the specified direction. -bool wxWindowBase::Navigate(int flags) +// Navigates in the specified direction inside this window +bool wxWindowBase::DoNavigateIn(int flags) { +#ifdef wxHAS_NATIVE_TAB_TRAVERSAL + // native code doesn't process our wxNavigationKeyEvents anyhow + wxUnusedVar(flags); + return false; +#else // !wxHAS_NATIVE_TAB_TRAVERSAL wxNavigationKeyEvent eventNav; eventNav.SetFlags(flags); - eventNav.SetEventObject(this); - if ( GetParent()->GetEventHandler()->ProcessEvent(eventNav) ) - { - return true; - } - return false; + eventNav.SetEventObject(FindFocus()); + return GetEventHandler()->ProcessEvent(eventNav); +#endif // wxHAS_NATIVE_TAB_TRAVERSAL/!wxHAS_NATIVE_TAB_TRAVERSAL } -void wxWindowBase::DoMoveInTabOrder(wxWindow *win, MoveKind move) +void wxWindowBase::DoMoveInTabOrder(wxWindow *win, WindowOrder move) { // check that we're not a top level window wxCHECK_RET( GetParent(), @@ -2543,7 +2700,7 @@ void wxWindowBase::DoMoveInTabOrder(wxWindow *win, MoveKind move) // can't just move the node around wxWindow *self = (wxWindow *)this; siblings.DeleteObject(self); - if ( move == MoveAfter ) + if ( move == OrderAfter ) { i = i->GetNext(); } @@ -2552,7 +2709,7 @@ void wxWindowBase::DoMoveInTabOrder(wxWindow *win, MoveKind move) { siblings.Insert(i, self); } - else // MoveAfter and win was the last sibling + else // OrderAfter and win was the last sibling { siblings.Append(self); } @@ -3022,3 +3179,125 @@ 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); +}