]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/gbsizer.cpp
Fix background drawing in EVT_ERASE_BACKGROUND handler in erase sample.
[wxWidgets.git] / src / common / gbsizer.cpp
index 26775f835e3cfb17b7d7d0d60f8c3973c3b02ca7..773c0f0ebc8ed1153f4f7a0781f5beab584e87fe 100644 (file)
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        gbsizer.cpp
+// Name:        src/common/gbsizer.cpp
 // Purpose:     wxGridBagSizer:  A sizer that can lay out items in a grid,
 //              with items at specified cells, and with the option of row
 //              and/or column spanning
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
-
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
-#pragma implementation "gbsizer.h"
-#endif
-
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
@@ -46,7 +41,7 @@ wxGBSizerItem::wxGBSizerItem( int width,
     : wxSizerItem(width, height, 0, flag, border, userData),
       m_pos(pos),
       m_span(span),
-      m_sizer(NULL)
+      m_gbsizer(NULL)
 {
 }
 
@@ -60,7 +55,7 @@ wxGBSizerItem::wxGBSizerItem( wxWindow *window,
     : wxSizerItem(window, 0, flag, border, userData),
       m_pos(pos),
       m_span(span),
-      m_sizer(NULL)
+      m_gbsizer(NULL)
 {
 }
 
@@ -74,15 +69,14 @@ wxGBSizerItem::wxGBSizerItem( wxSizer *sizer,
     : wxSizerItem(sizer, 0, flag, border, userData),
       m_pos(pos),
       m_span(span),
-      m_sizer(NULL)
+      m_gbsizer(NULL)
 {
 }
 
 wxGBSizerItem::wxGBSizerItem()
     : wxSizerItem(),
       m_pos(-1,-1),
-      m_span(-1,-1),
-      m_sizer(NULL)
+      m_gbsizer(NULL)
 {
 }
 
@@ -104,9 +98,9 @@ void wxGBSizerItem::GetSpan(int& rowspan, int& colspan) const
 
 bool wxGBSizerItem::SetPos( const wxGBPosition& pos )
 {
-    if (m_sizer)
+    if (m_gbsizer)
     {
-        wxCHECK_MSG( !m_sizer->CheckForIntersection(pos, m_span, this), false,
+        wxCHECK_MSG( !m_gbsizer->CheckForIntersection(pos, m_span, this), false,
                  wxT("An item is already at that position") );
     }
     m_pos = pos;
@@ -115,9 +109,9 @@ bool wxGBSizerItem::SetPos( const wxGBPosition& pos )
 
 bool wxGBSizerItem::SetSpan( const wxGBSpan& span )
 {
-    if (m_sizer)
+    if (m_gbsizer)
     {
-        wxCHECK_MSG( !m_sizer->CheckForIntersection(m_pos, span, this), false,
+        wxCHECK_MSG( !m_gbsizer->CheckForIntersection(m_pos, span, this), false,
                  wxT("An item is already at that position") );
     }
     m_span = span;
@@ -177,67 +171,90 @@ void wxGBSizerItem::GetEndPos(int& row, int& col)
 wxGridBagSizer::wxGridBagSizer(int vgap, int hgap )
     : wxFlexGridSizer(1, vgap, hgap),
       m_emptyCellSize(10,20)
-    
+
 {
 }
 
 
-bool wxGridBagSizer::Add( wxWindow *window,
-                          const wxGBPosition& pos, const wxGBSpan& span,
-                          int flag, int border,  wxObject* userData )
+wxSizerItem* wxGridBagSizer::Add( wxWindow *window,
+                                  const wxGBPosition& pos, const wxGBSpan& span,
+                                  int flag, int border,  wxObject* userData )
 {
     wxGBSizerItem* item = new wxGBSizerItem(window, pos, span, flag, border, userData);
     if ( Add(item) )
-        return true;
+        return item;
     else
     {
         delete item;
-        return false;
+        return NULL;
     }
 }
 
-bool wxGridBagSizer::Add( wxSizer *sizer,
+wxSizerItem* wxGridBagSizer::Add( wxSizer *sizer,
                           const wxGBPosition& pos, const wxGBSpan& span,
                           int flag, int border,  wxObject* userData )
 {
     wxGBSizerItem* item = new wxGBSizerItem(sizer, pos, span, flag, border, userData);
     if ( Add(item) )
-        return true;
+        return item;
     else
     {
         delete item;
-        return false;
+        return NULL;
     }
 }
 
-bool wxGridBagSizer::Add( int width, int height,
+wxSizerItem* wxGridBagSizer::Add( int width, int height,
                           const wxGBPosition& pos, const wxGBSpan& span,
                           int flag, int border,  wxObject* userData )
 {
     wxGBSizerItem* item = new wxGBSizerItem(width, height, pos, span, flag, border, userData);
     if ( Add(item) )
-        return true;
+        return item;
     else
     {
         delete item;
-        return false;
+        return NULL;
     }
 }
 
-bool wxGridBagSizer::Add( wxGBSizerItem *item )
+wxSizerItem* wxGridBagSizer::Add( wxGBSizerItem *item )
 {
-        m_children.Append(item);
-    item->SetSizer(this);
+    wxCHECK_MSG( !CheckForIntersection(item), NULL,
+                 wxT("An item is already at that position") );
+    m_children.Append(item);
+    item->SetGBSizer(this);
     if ( item->GetWindow() )
         item->GetWindow()->SetContainingSizer( this );
 
-    return true;
+    // extend the number of rows/columns of the underlying wxFlexGridSizer if
+    // necessary
+    int row, col;
+    item->GetEndPos(row, col);
+    row++;
+    col++;
+
+    if ( row > GetRows() )
+        SetRows(row);
+    if ( col > GetCols() )
+        SetCols(col);
+
+    return item;
 }
 
 
 
 //---------------------------------------------------------------------------
 
+wxSize wxGridBagSizer::GetCellSize(int row, int col) const
+{
+    wxCHECK_MSG( (row < m_rows) && (col < m_cols),
+                 wxDefaultSize,
+                 wxT("Invalid cell."));
+    return wxSize( m_colWidths[col], m_rowHeights[row] );
+}
+
+
 wxGBPosition wxGridBagSizer::GetItemPosition(wxWindow *window)
 {
     wxGBPosition badpos(-1,-1);
@@ -260,8 +277,8 @@ wxGBPosition wxGridBagSizer::GetItemPosition(size_t index)
 {
     wxGBPosition badpos(-1,-1);
     wxSizerItemList::compatibility_iterator node = m_children.Item( index );
-    wxCHECK_MSG( node, badpos, _T("Failed to find item.") );
-    wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();    
+    wxCHECK_MSG( node, badpos, wxT("Failed to find item.") );
+    wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
     return item->GetPos();
 }
 
@@ -286,8 +303,8 @@ bool wxGridBagSizer::SetItemPosition(wxSizer *sizer, const wxGBPosition& pos)
 bool wxGridBagSizer::SetItemPosition(size_t index, const wxGBPosition& pos)
 {
     wxSizerItemList::compatibility_iterator node = m_children.Item( index );
-    wxCHECK_MSG( node, false, _T("Failed to find item.") );
-    wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();    
+    wxCHECK_MSG( node, false, wxT("Failed to find item.") );
+    wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
     return item->SetPos(pos);
 }
 
@@ -297,7 +314,7 @@ wxGBSpan wxGridBagSizer::GetItemSpan(wxWindow *window)
 {
     wxGBSpan badspan(-1,-1);
     wxGBSizerItem* item = FindItem(window);
-    wxCHECK_MSG( item, badspan, _T("Failed to find item.") );
+    wxCHECK_MSG( item, badspan, wxT("Failed to find item.") );
     return item->GetSpan();
 }
 
@@ -306,7 +323,7 @@ wxGBSpan wxGridBagSizer::GetItemSpan(wxSizer *sizer)
 {
     wxGBSpan badspan(-1,-1);
     wxGBSizerItem* item = FindItem(sizer);
-    wxCHECK_MSG( item, badspan, _T("Failed to find item.") );
+    wxCHECK_MSG( item, badspan, wxT("Failed to find item.") );
     return item->GetSpan();
 }
 
@@ -315,8 +332,8 @@ wxGBSpan wxGridBagSizer::GetItemSpan(size_t index)
 {
     wxGBSpan badspan(-1,-1);
     wxSizerItemList::compatibility_iterator node = m_children.Item( index );
-    wxCHECK_MSG( node, badspan, _T("Failed to find item.") );
-    wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();    
+    wxCHECK_MSG( node, badspan, wxT("Failed to find item.") );
+    wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
     return item->GetSpan();
 }
 
@@ -341,8 +358,8 @@ bool wxGridBagSizer::SetItemSpan(wxSizer *sizer, const wxGBSpan& span)
 bool wxGridBagSizer::SetItemSpan(size_t index, const wxGBSpan& span)
 {
     wxSizerItemList::compatibility_iterator node = m_children.Item( index );
-    wxCHECK_MSG( node, false, _T("Failed to find item.") );
-    wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();    
+    wxCHECK_MSG( node, false, wxT("Failed to find item.") );
+    wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
     return item->SetSpan(span);
 }
 
@@ -395,6 +412,24 @@ wxGBSizerItem* wxGridBagSizer::FindItemAtPosition(const wxGBPosition& pos)
 
 
 
+wxGBSizerItem* wxGridBagSizer::FindItemAtPoint(const wxPoint& pt)
+{
+    wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
+    while (node)
+    {
+        wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
+        wxRect rect(item->GetPosition(), item->GetSize());
+        rect.Inflate(m_hgap, m_vgap);
+        if ( rect.Contains(pt) )
+            return item;
+        node = node->GetNext();
+    }
+    return NULL;
+}
+
+
+
+
 wxGBSizerItem* wxGridBagSizer::FindItemWithData(const wxObject* userData)
 {
     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
@@ -418,7 +453,7 @@ wxGBSizerItem* wxGridBagSizer::FindItemWithData(const wxObject* userData)
 wxSize wxGridBagSizer::CalcMin()
 {
     int idx;
-    
+
     if (m_children.GetCount() == 0)
         return m_emptyCellSize;
 
@@ -432,14 +467,14 @@ wxSize wxGridBagSizer::CalcMin()
         if ( item->IsShown() )
         {
             int row, col, endrow, endcol;
-        
+
             item->GetPos(row, col);
             item->GetEndPos(endrow, endcol);
 
-            // fill heights and widths upto this item if needed
-            while ( m_rowHeights.GetCount() <= (size_t)endrow )
+            // fill heights and widths up to this item if needed
+            while ( (int)m_rowHeights.GetCount() <= endrow )
                 m_rowHeights.Add(m_emptyCellSize.GetHeight());
-            while ( m_colWidths.GetCount() <= (size_t)endcol )
+            while ( (int)m_colWidths.GetCount() <= endcol )
                 m_colWidths.Add(m_emptyCellSize.GetWidth());
 
             // See if this item increases the size of its row(s) or col(s)
@@ -452,20 +487,22 @@ wxSize wxGridBagSizer::CalcMin()
         node = node->GetNext();
     }
 
+    AdjustForOverflow();
     AdjustForFlexDirection();
 
     // Now traverse the heights and widths arrays calcing the totals, including gaps
     int width = 0;
-    int ncols = m_colWidths.GetCount();
-    for (idx=0; idx < ncols; idx++)
-        width += m_colWidths[idx] + ( idx == ncols-1 ? 0 : m_hgap );
+    m_cols = m_colWidths.GetCount();
+    for (idx=0; idx < m_cols; idx++)
+        width += m_colWidths[idx] + ( idx == m_cols-1 ? 0 : m_hgap );
 
     int height = 0;
-    int nrows = m_rowHeights.GetCount();
-    for (idx=0; idx < nrows; idx++)
-        height += m_rowHeights[idx] + ( idx == nrows-1 ? 0 : m_vgap );
+    m_rows = m_rowHeights.GetCount();
+    for (idx=0; idx < m_rows; idx++)
+        height += m_rowHeights[idx] + ( idx == m_rows-1 ? 0 : m_vgap );
 
-    return wxSize(width, height);   
+    m_calculatedMinSize = wxSize(width, height);
+    return m_calculatedMinSize;
 }
 
 
@@ -475,23 +512,20 @@ void wxGridBagSizer::RecalcSizes()
     if (m_children.GetCount() == 0)
         return;
 
-    // Calculates minsize and populates m_rowHeights and m_colWidths
-    wxSize  minsz( CalcMin() ); 
-
     wxPoint pt( GetPosition() );
     wxSize  sz( GetSize() );
-   
-    int nrows = m_rowHeights.GetCount();
-    int ncols = m_colWidths.GetCount();
+
+    m_rows = m_rowHeights.GetCount();
+    m_cols = m_colWidths.GetCount();
     int idx, width, height;
 
-    AdjustForGrowables(sz, minsz, nrows, ncols);
+    AdjustForGrowables(sz);
 
     // Find the start positions on the window of the rows and columns
     wxArrayInt rowpos;
-    rowpos.Add(0, nrows);
+    rowpos.Add(0, m_rows);
     int y = pt.y;
-    for (idx=0; idx < nrows; idx++)
+    for (idx=0; idx < m_rows; idx++)
     {
         height = m_rowHeights[idx] + m_vgap;
         rowpos[idx] = y;
@@ -499,9 +533,9 @@ void wxGridBagSizer::RecalcSizes()
     }
 
     wxArrayInt colpos;
-    colpos.Add(0, ncols);
+    colpos.Add(0, m_cols);
     int x = pt.x;
-    for (idx=0; idx < ncols; idx++)
+    for (idx=0; idx < m_cols; idx++)
     {
         width = m_colWidths[idx] + m_hgap;
         colpos[idx] = x;
@@ -515,24 +549,122 @@ void wxGridBagSizer::RecalcSizes()
     {
         int row, col, endrow, endcol;
         wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
-        item->GetPos(row, col);
-        item->GetEndPos(endrow, endcol);
-
-        height = 0;
-        for(idx=row; idx <= endrow; idx++)
-            height += m_rowHeights[idx] + m_vgap;
-        
-        width = 0;
-        for (idx=col; idx <= endcol; idx++)
-            width += m_colWidths[idx] + m_hgap;
-    
-        SetItemBounds(item, colpos[col], rowpos[row], width, height);
+
+        if ( item->IsShown() )
+        {
+            item->GetPos(row, col);
+            item->GetEndPos(endrow, endcol);
+
+            height = 0;
+            for(idx=row; idx <= endrow; idx++)
+                height += m_rowHeights[idx];
+            height += (endrow - row) * m_vgap; // add a vgap for every row spanned
+
+            width = 0;
+            for (idx=col; idx <= endcol; idx++)
+                width += m_colWidths[idx];
+            width += (endcol - col) * m_hgap; // add a hgap for every col spanned
+
+            SetItemBounds(item, colpos[col], rowpos[row], width, height);
+        }
 
         node = node->GetNext();
-    }        
+    }
 }
 
 
+// Sometimes CalcMin can result in some rows or cols having too much space in
+// them because as it traverses the items it makes some assumptions when
+// items span to other cells.  But those assumptions can become invalid later
+// on when other items are fitted into the same rows or columns that the
+// spanning item occupies. This method tries to find those situations and
+// fixes them.
+void wxGridBagSizer::AdjustForOverflow()
+{
+    int row, col;
+
+    for (row=0; row<(int)m_rowHeights.GetCount(); row++)
+    {
+        int rowExtra=INT_MAX;
+        int rowHeight = m_rowHeights[row];
+        for (col=0; col<(int)m_colWidths.GetCount(); col++)
+        {
+            wxGBPosition pos(row,col);
+            wxGBSizerItem* item = FindItemAtPosition(pos);
+            if ( !item || !item->IsShown() )
+                continue;
+
+            int endrow, endcol;
+            item->GetEndPos(endrow, endcol);
+
+            // If the item starts in this position and doesn't span rows, then
+            // just look at the whole item height
+            if ( item->GetPos() == pos && endrow == row )
+            {
+                int itemHeight = item->CalcMin().GetHeight();
+                rowExtra = wxMin(rowExtra, rowHeight - itemHeight);
+                continue;
+            }
+
+            // Otherwise, only look at spanning items if they end on this row
+            if ( endrow == row )
+            {
+                // first deduct the portions of the item that are on prior rows
+                int itemHeight = item->CalcMin().GetHeight();
+                for (int r=item->GetPos().GetRow(); r<row; r++)
+                    itemHeight -= (m_rowHeights[r] + GetHGap());
+
+                if ( itemHeight < 0 )
+                    itemHeight = 0;
+
+                // and check how much is left
+                rowExtra = wxMin(rowExtra, rowHeight - itemHeight);
+            }
+        }
+        if ( rowExtra && rowExtra != INT_MAX )
+            m_rowHeights[row] -= rowExtra;
+    }
+
+    // Now do the same thing for columns
+    for (col=0; col<(int)m_colWidths.GetCount(); col++)
+    {
+        int colExtra=INT_MAX;
+        int colWidth = m_colWidths[col];
+        for (row=0; row<(int)m_rowHeights.GetCount(); row++)
+        {
+            wxGBPosition pos(row,col);
+            wxGBSizerItem* item = FindItemAtPosition(pos);
+            if ( !item || !item->IsShown() )
+                continue;
+
+            int endrow, endcol;
+            item->GetEndPos(endrow, endcol);
+
+            if ( item->GetPos() == pos && endcol == col )
+            {
+                int itemWidth = item->CalcMin().GetWidth();
+                colExtra = wxMin(colExtra, colWidth - itemWidth);
+                continue;
+            }
+
+            if ( endcol == col )
+            {
+                int itemWidth = item->CalcMin().GetWidth();
+                for (int c=item->GetPos().GetCol(); c<col; c++)
+                    itemWidth -= (m_colWidths[c] + GetVGap());
+
+                if ( itemWidth < 0 )
+                    itemWidth = 0;
+
+                colExtra = wxMin(colExtra, colWidth - itemWidth);
+            }
+        }
+        if ( colExtra && colExtra != INT_MAX )
+            m_colWidths[col] -= colExtra;
+    }
+
+
+}
 
 //---------------------------------------------------------------------------
 
@@ -551,7 +683,7 @@ bool wxGridBagSizer::CheckForIntersection(const wxGBPosition& pos, const wxGBSpa
 
         if ( excludeItem && item == excludeItem )
             continue;
-        
+
         if ( item->Intersects(pos, span) )
             return true;
 
@@ -584,19 +716,19 @@ wxGBPosition wxGridBagSizer::FindEmptyCell()
 // we'll try to make them automatically select a location for the item
 // anyway.
 
-void wxGridBagSizer::Add( wxWindow *window, int, int flag, int border, wxObject* userData )
+wxSizerItem* wxGridBagSizer::Add( wxWindow *window, int, int flag, int border, wxObject* userData )
 {
-    Add(window, FindEmptyCell(), wxDefaultSpan, flag, border, userData);
+    return Add(window, FindEmptyCell(), wxDefaultSpan, flag, border, userData);
 }
 
-void wxGridBagSizer::Add( wxSizer *sizer, int, int flag, int border, wxObject* userData )
+wxSizerItem* wxGridBagSizer::Add( wxSizer *sizer, int, int flag, int border, wxObject* userData )
 {
-    Add(sizer, FindEmptyCell(), wxDefaultSpan, flag, border, userData);
+    return Add(sizer, FindEmptyCell(), wxDefaultSpan, flag, border, userData);
 }
 
-void wxGridBagSizer::Add( int width, int height, int, int flag, int border, wxObject* userData )
+wxSizerItem* wxGridBagSizer::Add( int width, int height, int, int flag, int border, wxObject* userData )
 {
-    Add(width, height, FindEmptyCell(), wxDefaultSpan, flag, border, userData);
+    return Add(width, height, FindEmptyCell(), wxDefaultSpan, flag, border, userData);
 }
 
 
@@ -605,33 +737,60 @@ void wxGridBagSizer::Add( int width, int height, int, int flag, int border, wxOb
 // this class and should not be used.  Their implementation in this class
 // simply fails.
 
-void wxGridBagSizer::Add( wxSizerItem * )
-{ wxFAIL_MSG(wxT("Invalid Add form called.")); }
+wxSizerItem* wxGridBagSizer::Add( wxSizerItem * )
+{
+    wxFAIL_MSG(wxT("Invalid Add form called."));
+    return NULL;
+}
 
-void wxGridBagSizer::Prepend( wxWindow *, int, int, int, wxObject*  )
-{ wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer.")); }
+wxSizerItem* wxGridBagSizer::Prepend( wxWindow *, int, int, int, wxObject*  )
+{
+    wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer."));
+    return NULL;
+}
 
-void wxGridBagSizer::Prepend( wxSizer *, int, int, int, wxObject*  )
-{ wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer.")); }
+wxSizerItem* wxGridBagSizer::Prepend( wxSizer *, int, int, int, wxObject*  )
+{
+    wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer."));
+    return NULL;
+}
 
-void wxGridBagSizer::Prepend( int, int, int, int, int, wxObject*  )
-{ wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer.")); }
+wxSizerItem* wxGridBagSizer::Prepend( int, int, int, int, int, wxObject*  )
+{
+    wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer."));
+    return NULL;
+}
 
-void wxGridBagSizer::Prepend( wxSizerItem * )
-{ wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer.")); }
+wxSizerItem* wxGridBagSizer::Prepend( wxSizerItem * )
+{
+    wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer."));
+    return NULL;
+}
 
 
-void wxGridBagSizer::Insert( size_t, wxWindow *, int, int, int, wxObject*  )
-{ wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer.")); }
+wxSizerItem* wxGridBagSizer::Insert( size_t, wxWindow *, int, int, int, wxObject*  )
+{
+    wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer."));
+    return NULL;
+}
 
-void wxGridBagSizer::Insert( size_t, wxSizer *, int, int, int, wxObject*  )
-{ wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer.")); }
+wxSizerItem* wxGridBagSizer::Insert( size_t, wxSizer *, int, int, int, wxObject*  )
+{
+    wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer."));
+    return NULL;
+}
 
-void wxGridBagSizer::Insert( size_t, int, int, int, int, int, wxObject*  )
-{ wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer.")); }
+wxSizerItem* wxGridBagSizer::Insert( size_t, int, int, int, int, int, wxObject*  )
+{
+    wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer."));
+    return NULL;
+}
 
-void wxGridBagSizer::Insert( size_t, wxSizerItem * )
-{ wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer.")); }
+wxSizerItem* wxGridBagSizer::Insert( size_t, wxSizerItem * )
+{
+    wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer."));
+    return NULL;
+}
 
 
 //---------------------------------------------------------------------------