X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/c52b96359b639cda516615f9433d3b20b35443b5..8aea37a9df3a2f5ca949ff466bbf698a4b2e3062:/src/common/wincmn.cpp diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp index 9b95e33b7b..1de80827bd 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; @@ -193,11 +198,6 @@ wxWindowBase::wxWindowBase() m_scrollHelper = (wxScrollHelper *) NULL; - m_minVirtualWidth = - m_maxVirtualWidth = wxDefaultCoord; - m_minVirtualHeight = - m_maxVirtualHeight = wxDefaultCoord; - m_windowVariant = wxWINDOW_VARIANT_NORMAL; #if wxUSE_SYSTEM_OPTIONS if ( wxSystemOptions::HasOption(wxWINDOW_DEFAULT_VARIANT) ) @@ -211,6 +211,8 @@ wxWindowBase::wxWindowBase() // VZ: this one shouldn't exist... m_isBeingDeleted = false; + + m_freezeCount = 0; } // common part of window creation process @@ -244,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 { @@ -307,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 @@ -322,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 @@ -367,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() @@ -392,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() @@ -608,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 ) @@ -648,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: @@ -711,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 ) @@ -771,26 +804,20 @@ void wxWindowBase::DoSetSizeHints( int minW, int minH, } -void wxWindowBase::SetVirtualSizeHints( int minW, int minH, - int maxW, int maxH ) +#if WXWIN_COMPATIBILITY_2_8 +void wxWindowBase::SetVirtualSizeHints(int WXUNUSED(minW), int WXUNUSED(minH), + int WXUNUSED(maxW), int WXUNUSED(maxH)) { - m_minVirtualWidth = minW; - m_maxVirtualWidth = maxW; - m_minVirtualHeight = minH; - m_maxVirtualHeight = maxH; } -void wxWindowBase::DoSetVirtualSize( int x, int y ) +void wxWindowBase::SetVirtualSizeHints(const wxSize& WXUNUSED(minsize), + const wxSize& WXUNUSED(maxsize)) { - if ( m_minVirtualWidth != wxDefaultCoord && m_minVirtualWidth > x ) - x = m_minVirtualWidth; - if ( m_maxVirtualWidth != wxDefaultCoord && m_maxVirtualWidth < x ) - x = m_maxVirtualWidth; - if ( m_minVirtualHeight != wxDefaultCoord && m_minVirtualHeight > y ) - y = m_minVirtualHeight; - if ( m_maxVirtualHeight != wxDefaultCoord && m_maxVirtualHeight < y ) - y = m_maxVirtualHeight; +} +#endif // WXWIN_COMPATIBILITY_2_8 +void wxWindowBase::DoSetVirtualSize( int x, int y ) +{ m_virtualSize = wxSize(x, y); } @@ -821,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 // ---------------------------------------------------------------------------- @@ -892,8 +936,11 @@ bool wxWindowBase::Enable(bool enable) bool wxWindowBase::IsShownOnScreen() const { + // A window is shown on screen if it itself is shown and so are all its + // parents. But if a window is toplevel one, then its always visible on + // screen if IsShown() returns true, even if it has a hidden parent. return IsShown() && - (GetParent() == NULL || GetParent()->IsShownOnScreen()); + (IsTopLevel() || GetParent() == NULL || GetParent()->IsShownOnScreen()); } // ---------------------------------------------------------------------------- @@ -905,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 // ---------------------------------------------------------------------------- @@ -920,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); } @@ -1051,6 +1157,11 @@ bool wxWindowBase::RemoveEventHandler(wxEvtHandler *handler) return false; } +bool wxWindowBase::HandleWindowEvent(wxEvent& event) const +{ + return GetEventHandler()->SafelyProcessEvent(event); +} + // ---------------------------------------------------------------------------- // colours, fonts &c // ---------------------------------------------------------------------------- @@ -1111,7 +1222,7 @@ wxWindowBase::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant)) wxColour wxWindowBase::GetBackgroundColour() const { - if ( !m_backgroundColour.Ok() ) + if ( !m_backgroundColour.IsOk() ) { wxASSERT_MSG( !m_hasBgCol, _T("we have invalid explicit bg colour?") ); @@ -1139,7 +1250,7 @@ wxColour wxWindowBase::GetForegroundColour() const { wxColour colFg = GetDefaultAttributes().colFg; - if ( !colFg.Ok() ) + if ( !colFg.IsOk() ) colFg = GetClassDefaultAttributes().colFg; return colFg; @@ -1153,7 +1264,7 @@ bool wxWindowBase::SetBackgroundColour( const wxColour &colour ) if ( colour == m_backgroundColour ) return false; - m_hasBgCol = colour.Ok(); + m_hasBgCol = colour.IsOk(); if ( m_backgroundStyle != wxBG_STYLE_CUSTOM ) m_backgroundStyle = m_hasBgCol ? wxBG_STYLE_COLOUR : wxBG_STYLE_SYSTEM; @@ -1168,7 +1279,7 @@ bool wxWindowBase::SetForegroundColour( const wxColour &colour ) if (colour == m_foregroundColour ) return false; - m_hasFgCol = colour.Ok(); + m_hasFgCol = colour.IsOk(); m_inheritFgCol = m_hasFgCol; m_foregroundColour = colour; SetThemeEnabled( !m_hasFgCol && !m_backgroundColour.Ok() ); @@ -1193,12 +1304,12 @@ bool wxWindowBase::SetCursor(const wxCursor& cursor) wxFont wxWindowBase::GetFont() const { // logic is the same as in GetBackgroundColour() - if ( !m_font.Ok() ) + if ( !m_font.IsOk() ) { wxASSERT_MSG( !m_hasFont, _T("we have invalid explicit font?") ); wxFont font = GetDefaultAttributes().font; - if ( !font.Ok() ) + if ( !font.IsOk() ) font = GetClassDefaultAttributes().font; return font; @@ -1216,7 +1327,7 @@ bool wxWindowBase::SetFont(const wxFont& font) } m_font = font; - m_hasFont = font.Ok(); + m_hasFont = font.IsOk(); m_inheritFont = m_hasFont; InvalidateBestSize(); @@ -1315,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__ @@ -1611,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) @@ -1621,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 @@ -1644,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; @@ -2248,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 // @@ -2292,7 +2438,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)); } @@ -2502,7 +2648,10 @@ void wxWindowBase::ReleaseMouse() 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; @@ -2599,8 +2748,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; } @@ -2669,12 +2818,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 @@ -2720,6 +2890,13 @@ 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(); +} + // ---------------------------------------------------------------------------- // global functions // ---------------------------------------------------------------------------- @@ -3174,125 +3351,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); -}