X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/d0154e3a5a3353bc9f26f8ef1ca7db44ac4835e4..f71adb50291d95932159cbd3afa6a0a6fb86b20e:/src/generic/datavgen.cpp diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index ec68fc76a9..df22e987ea 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -48,6 +48,7 @@ #include "wx/imaglist.h" #include "wx/headerctrl.h" #include "wx/dnd.h" +#include "wx/stopwatch.h" //----------------------------------------------------------------------------- // classes @@ -761,10 +762,10 @@ bool wxDataViewTextRenderer::Render(wxRect rect, wxDC *dc, int state) wxSize wxDataViewTextRenderer::GetSize() const { - const wxDataViewCtrl *view = GetView(); if (!m_text.empty()) - return view->wxWindowBase::GetTextExtent( m_text ); - return wxSize(wxDVC_DEFAULT_RENDERER_SIZE,wxDVC_DEFAULT_RENDERER_SIZE); + return GetTextExtent(m_text); + else + return wxSize(wxDVC_DEFAULT_RENDERER_SIZE,wxDVC_DEFAULT_RENDERER_SIZE); } // --------------------------------------------------------- @@ -1016,11 +1017,7 @@ bool wxDataViewDateRenderer::Render( wxRect cell, wxDC *dc, int state ) wxSize wxDataViewDateRenderer::GetSize() const { - const wxDataViewCtrl* view = GetView(); - wxString tmp = m_date.FormatDate(); - wxCoord x,y,d; - view->GetTextExtent( tmp, &x, &y, &d ); - return wxSize(x,y+d); + return GetTextExtent(m_date.FormatDate()); } void wxDataViewDateRenderer::WXOnActivate(wxDataViewModel *model, @@ -1085,15 +1082,13 @@ bool wxDataViewIconTextRenderer::Render(wxRect rect, wxDC *dc, int state) wxSize wxDataViewIconTextRenderer::GetSize() const { - const wxDataViewCtrl *view = GetView(); if (!m_value.GetText().empty()) { - int x,y; - view->GetTextExtent( m_value.GetText(), &x, &y ); + wxSize size = GetTextExtent(m_value.GetText()); if (m_value.GetIcon().IsOk()) - x += m_value.GetIcon().GetWidth() + 4; - return wxSize( x, y ); + size.x += m_value.GetIcon().GetWidth() + 4; + return size; } return wxSize(80,20); } @@ -1278,7 +1273,7 @@ void wxDataViewRenameTimer::Notify() //----------------------------------------------------------------------------- // The tree building helper, declared firstly -static void BuildTreeHelper( wxDataViewModel * model, wxDataViewItem & item, +static void BuildTreeHelper( const wxDataViewModel * model, const wxDataViewItem & item, wxDataViewTreeNode * node); int LINKAGEMODE wxDataViewSelectionCmp( unsigned int row1, unsigned int row2 ) @@ -1559,14 +1554,8 @@ wxBitmap wxDataViewMainWindow::CreateItemBitmap( unsigned int row, int &indent ) if (column == expander) width -= indent; - wxVariant value; wxDataViewItem item = GetItemByRow( row ); - model->GetValue( value, item, column->GetModelColumn()); - cell->SetValue( value ); - - wxDataViewItemAttr attr; - model->GetAttr(item, column->GetModelColumn(), attr); - cell->SetAttr(attr); + cell->PrepareForItem(model, item, column->GetModelColumn()); wxRect item_rect(x, 0, width, height); item_rect.Deflate(PADDING_RIGHTLEFT, 0); @@ -1748,7 +1737,6 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) for (unsigned int item = item_start; item < item_last; item++) { // get the cell value and set it into the renderer - wxVariant value; wxDataViewTreeNode *node = NULL; wxDataViewItem dataitem; @@ -1769,12 +1757,7 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) dataitem = wxDataViewItem( wxUIntToPtr(item+1) ); } - model->GetValue( value, dataitem, col->GetModelColumn()); - cell->SetValue( value ); - - wxDataViewItemAttr attr; - model->GetAttr(dataitem, col->GetModelColumn(), attr); - cell->SetAttr(attr); + cell->PrepareForItem(model, dataitem, col->GetModelColumn()); // update cell_rect cell_rect.y = GetLineStart( item ); @@ -1910,8 +1893,8 @@ bool Walker( wxDataViewTreeNode * node, DoJob & func ) ; } - wxDataViewTreeNodes nodes = node->GetNodes(); - wxDataViewTreeLeaves leaves = node->GetChildren(); + const wxDataViewTreeNodes& nodes = node->GetNodes(); + const wxDataViewTreeLeaves& leaves = node->GetChildren(); int len_nodes = nodes.GetCount(); int len = leaves.GetCount(); @@ -2524,12 +2507,10 @@ int wxDataViewMainWindow::GetLineStart( unsigned int row ) const !model->HasContainerColumns(item)) continue; // skip it! - wxVariant value; - model->GetValue( value, item, column->GetModelColumn() ); - wxDataViewRenderer *renderer = const_cast(column->GetRenderer()); - renderer->SetValue( value ); + renderer->PrepareForItem(model, item, column->GetModelColumn()); + height = wxMax( height, renderer->GetSize().y ); } @@ -2587,12 +2568,10 @@ int wxDataViewMainWindow::GetLineAt( unsigned int y ) const !model->HasContainerColumns(item)) continue; // skip it! - wxVariant value; - model->GetValue( value, item, column->GetModelColumn() ); - wxDataViewRenderer *renderer = const_cast(column->GetRenderer()); - renderer->SetValue( value ); + renderer->PrepareForItem(model, item, column->GetModelColumn()); + height = wxMax( height, renderer->GetSize().y ); } @@ -2640,12 +2619,10 @@ int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const !model->HasContainerColumns(item)) continue; // skip it! - wxVariant value; - model->GetValue( value, item, column->GetModelColumn() ); - wxDataViewRenderer *renderer = const_cast(column->GetRenderer()); - renderer->SetValue( value ); + renderer->PrepareForItem(model, item, column->GetModelColumn()); + height = wxMax( height, renderer->GetSize().y ); } @@ -2992,28 +2969,26 @@ void wxDataViewMainWindow::Collapse(unsigned int row) wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item ) { - wxDataViewModel * model = GetOwner()->GetModel(); + const wxDataViewModel * model = GetOwner()->GetModel(); if( model == NULL ) return NULL; if (!item.IsOk()) return m_root; - // Compose the a parent-chain of the finding item - ItemList list; - list.DeleteContents( true ); + // Compose the parent-chain for the item we are looking for + wxVector parentChain; wxDataViewItem it( item ); while( it.IsOk() ) { - wxDataViewItem * pItem = new wxDataViewItem( it ); - list.Insert( pItem ); - it = model->GetParent( it ); + parentChain.push_back(it); + it = model->GetParent(it); } // Find the item along the parent-chain. // This algorithm is designed to speed up the node-finding method - wxDataViewTreeNode * node = m_root; - for( ItemList::const_iterator iter = list.begin(); iter !=list.end(); iter++ ) + wxDataViewTreeNode* node = m_root; + for( unsigned iter = parentChain.size()-1; iter>=0; --iter ) { if( node->HasChildren() ) { @@ -3023,18 +2998,18 @@ wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item ::BuildTreeHelper(model, node->GetItem(), node); } - wxDataViewTreeNodes nodes = node->GetNodes(); - unsigned int i; + const wxDataViewTreeNodes& nodes = node->GetNodes(); bool found = false; - for (i = 0; i < nodes.GetCount(); i ++) + for (unsigned i = 0; i < nodes.GetCount(); ++i) { - if (nodes[i]->GetItem() == (**iter)) + wxDataViewTreeNode* currentNode = nodes[i]; + if (currentNode->GetItem() == parentChain[iter]) { - if (nodes[i]->GetItem() == item) - return nodes[i]; + if (currentNode->GetItem() == item) + return currentNode; - node = nodes[i]; + node = currentNode; found = true; break; } @@ -3154,7 +3129,7 @@ int wxDataViewMainWindow::RecalculateCount() class ItemToRowJob : public DoJob { public: - ItemToRowJob(const wxDataViewItem& item_, ItemList::const_iterator iter) + ItemToRowJob(const wxDataViewItem& item_, wxVector::reverse_iterator iter) : m_iter(iter), item(item_) { @@ -3170,7 +3145,7 @@ public: return DoJob::OK; } - if( node->GetItem() == **m_iter ) + if( node->GetItem() == *m_iter ) { m_iter++; return DoJob::CONT; @@ -3196,7 +3171,7 @@ public: { return ret -1; } private: - ItemList::const_iterator m_iter; + wxVector::reverse_iterator m_iter; wxDataViewItem item; int ret; @@ -3217,27 +3192,27 @@ int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item) const if( !item.IsOk() ) return -1; - // Compose the a parent-chain of the finding item - ItemList list; - wxDataViewItem * pItem; - list.DeleteContents( true ); + // Compose the parent-chain of the item we are looking for + wxVector parentChain; wxDataViewItem it( item ); while( it.IsOk() ) { - pItem = new wxDataViewItem( it ); - list.Insert( pItem ); - it = model->GetParent( it ); + parentChain.push_back(it); + it = model->GetParent(it); } - pItem = new wxDataViewItem( ); - list.Insert( pItem ); - ItemToRowJob job( item, list.begin() ); - Walker(m_root , job ); + // add an 'invalid' item to represent our 'invisible' root node + parentChain.push_back(wxDataViewItem()); + + // the parent chain was created by adding the deepest parent first. + // so if we want to start at the root node, we have to iterate backwards through the vector + ItemToRowJob job( item, parentChain.rbegin() ); + Walker( m_root, job ); return job.GetResult(); } } -static void BuildTreeHelper( wxDataViewModel * model, wxDataViewItem & item, +static void BuildTreeHelper( const wxDataViewModel * model, const wxDataViewItem & item, wxDataViewTreeNode * node) { if( !model->IsContainer( item ) ) @@ -3648,6 +3623,8 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) wxWindow *parent = GetParent(); wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId()); le.SetItem( item ); + le.SetColumn( col->GetModelColumn() ); + le.SetDataViewColumn( col ); le.SetEventObject(parent); le.SetModel(GetOwner()->GetModel()); @@ -3718,6 +3695,8 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) wxWindow *parent = GetParent(); wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, parent->GetId()); le.SetItem( item ); + le.SetColumn( col->GetModelColumn() ); + le.SetDataViewColumn( col ); le.SetEventObject(parent); le.SetModel(GetOwner()->GetModel()); le.SetValue(value); @@ -3811,9 +3790,8 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) if ( wxDataViewCustomRenderer *custom = cell->WXGetAsCustom() ) { // notify cell about click - wxVariant value; - model->GetValue( value, item, col->GetModelColumn() ); - custom->SetValue( value ); + custom->PrepareForItem(model, item, col->GetModelColumn()); + wxRect cell_rect( xpos, GetLineStart( current ), col->GetWidth(), GetLineHeight( current ) ); @@ -4146,37 +4124,136 @@ unsigned int wxDataViewCtrl::GetBestColumnWidth(int idx) const if ( m_colsBestWidths[idx] != 0 ) return m_colsBestWidths[idx]; - const unsigned count = m_clientArea->GetRowCount(); + const int count = m_clientArea->GetRowCount(); wxDataViewColumn *column = GetColumn(idx); wxDataViewRenderer *renderer = const_cast(column->GetRenderer()); - int max_width = 0; + class MaxWidthCalculator + { + public: + MaxWidthCalculator(wxDataViewMainWindow *clientArea, + wxDataViewRenderer *renderer, + const wxDataViewModel *model, + unsigned column) + : m_width(0), + m_clientArea(clientArea), + m_renderer(renderer), + m_model(model), + m_column(column) + { + } + + void UpdateWithWidth(int width) + { + m_width = wxMax(m_width, width); + } + + void UpdateWithRow(int row) + { + wxDataViewItem item = m_clientArea->GetItemByRow(row); + m_renderer->PrepareForItem(m_model, item, m_column); + m_width = wxMax(m_width, m_renderer->GetSize().x); + } + + int GetMaxWidth() const { return m_width; } + + private: + int m_width; + wxDataViewMainWindow *m_clientArea; + wxDataViewRenderer *m_renderer; + const wxDataViewModel *m_model; + unsigned m_column; + }; + + MaxWidthCalculator calculator(m_clientArea, renderer, + GetModel(), column->GetModelColumn()); if ( m_headerArea ) { - max_width = m_headerArea->GetTextExtent(column->GetTitle()).x; - + int header_width = m_headerArea->GetTextExtent(column->GetTitle()).x; // Labels on native MSW header are indented on both sides - max_width += wxRendererNative::Get().GetHeaderButtonMargin(m_headerArea); + header_width += + wxRendererNative::Get().GetHeaderButtonMargin(m_headerArea); + calculator.UpdateWithWidth(header_width); + } + + // The code below deserves some explanation. For very large controls, we + // simply can't afford to calculate sizes for all items, it takes too + // long. So the best we can do is to check the first and the last N/2 + // items in the control for some sufficiently large N and calculate best + // sizes from that. That can result in the calculated best width being too + // small for some outliers, but it's better to get slightly imperfect + // result than to wait several seconds after every update. To avoid highly + // visible miscalculations, we also include all currently visible items + // no matter what. Finally, the value of N is determined dynamically by + // measuring how much time we spent on the determining item widths so far. + +#if wxUSE_STOPWATCH + int top_part_end = count; + static const long CALC_TIMEOUT = 20/*ms*/; + // don't call wxStopWatch::Time() too often + static const unsigned CALC_CHECK_FREQ = 100; + wxStopWatch timer; +#else + // use some hard-coded limit, that's the best we can do without timer + int top_part_end = wxMin(500, count); +#endif // wxUSE_STOPWATCH/!wxUSE_STOPWATCH + + int row = 0; + + for ( row = 0; row < top_part_end; row++ ) + { +#if wxUSE_STOPWATCH + if ( row % CALC_CHECK_FREQ == CALC_CHECK_FREQ-1 && + timer.Time() > CALC_TIMEOUT ) + break; +#endif // wxUSE_STOPWATCH + calculator.UpdateWithRow(row); } - for ( unsigned row = 0; row < count; row++ ) + // row is the first unmeasured item now; that's out value of N/2 + + if ( row < count ) { - wxDataViewItem item = m_clientArea->GetItemByRow(row); + top_part_end = row; - wxVariant value; - GetModel()->GetValue(value, item, column->GetModelColumn()); - renderer->SetValue(value); + // add bottom N/2 items now: + const int bottom_part_start = wxMax(row, count - row); + for ( row = bottom_part_start; row < count; row++ ) + { + calculator.UpdateWithRow(row); + } + + // finally, include currently visible items in the calculation: + const wxPoint origin = CalcUnscrolledPosition(wxPoint(0, 0)); + int first_visible = m_clientArea->GetLineAt(origin.y); + int last_visible = m_clientArea->GetLineAt(origin.y + GetClientSize().y); - max_width = (unsigned)wxMax((int)max_width, renderer->GetSize().x); + first_visible = wxMax(first_visible, top_part_end); + last_visible = wxMin(bottom_part_start, last_visible); + + for ( row = first_visible; row < last_visible; row++ ) + { + calculator.UpdateWithRow(row); + } + + wxLogTrace("dataview", + "determined best size from %d top, %d bottom plus %d more visible items out of %d total", + top_part_end, + count - bottom_part_start, + wxMax(0, last_visible - first_visible), + count); } + int max_width = calculator.GetMaxWidth(); if ( max_width > 0 ) max_width += 2 * PADDING_RIGHTLEFT; const_cast(this)->m_colsBestWidths[idx] = max_width; return max_width; + + #undef MEASURE_ITEM } void wxDataViewCtrl::ColumnMoved(wxDataViewColumn * WXUNUSED(col),