#include "wx/osx/private.h"
#include "wx/osx/cocoa/dataview.h"
#include "wx/renderer.h"
+#include "wx/stopwatch.h"
// ============================================================================
// Constants used locally
);
// setting the size related parameters:
- const int width = column->GetWidthVariable();
int resizingMask;
if (column->IsResizeable())
{
: NSTableColumnNoResizing;
}
[nativeColumn setResizingMask:resizingMask];
- [nativeColumn setWidth:width];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
// setting the visibility:
[m_OutlineView addTableColumn:nativeColumn];
if (pos != static_cast<unsigned int>([m_OutlineView numberOfColumns]-1))
[m_OutlineView moveColumn:[m_OutlineView numberOfColumns]-1 toColumn:pos];
+
+ // set columns width now that it can be computed even for autosized columns:
+ columnPtr->SetWidth(columnPtr->GetWidthVariable());
+
// done:
return true;
}
+void wxCocoaDataViewControl::FitColumnWidthToContent(unsigned int pos)
+{
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
+ const int count = GetCount();
+ NSTableColumn *column = GetColumn(pos)->GetNativeData()->GetNativeColumnPtr();
+
+ class MaxWidthCalculator
+ {
+ public:
+ MaxWidthCalculator(wxCocoaOutlineView *view,
+ NSTableColumn *column, unsigned columnIndex)
+ : m_width(0),
+ m_view(view),
+ m_column(columnIndex),
+ m_indent(0)
+ {
+ // account for indentation in the column with expander
+ if ( column == [m_view outlineTableColumn] )
+ m_indent = [m_view indentationPerLevel];
+ }
+
+ void UpdateWithWidth(int width)
+ {
+ m_width = wxMax(m_width, width);
+ }
+
+ void UpdateWithRow(int row)
+ {
+ NSCell *cell = [m_view preparedCellAtColumn:m_column row:row];
+ unsigned cellWidth = [cell cellSize].width + 1/*round the float up*/;
+
+ if ( m_indent )
+ cellWidth += m_indent * ([m_view levelForRow:row] + 1);
+
+ m_width = wxMax(m_width, cellWidth);
+ }
+
+ int GetMaxWidth() const { return m_width; }
+
+ private:
+ int m_width;
+ wxCocoaOutlineView *m_view;
+ unsigned m_column;
+ int m_indent;
+ };
+
+ MaxWidthCalculator calculator(m_OutlineView, column, pos);
+
+ if ( [column headerCell] )
+ {
+ calculator.UpdateWithWidth([[column headerCell] cellSize].width + 1/*round the float up*/);
+ }
+
+ // 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);
+ }
+
+ // row is the first unmeasured item now; that's our value of N/2
+
+ if ( row < count )
+ {
+ top_part_end = row;
+
+ // 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 NSRange visible = [m_OutlineView rowsInRect:[m_OutlineView visibleRect]];
+ const int first_visible = wxMax(visible.location, top_part_end);
+ const int last_visible = wxMin(first_visible + visible.length, bottom_part_start);
+
+ 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);
+ }
+
+ [column setWidth:calculator.GetMaxWidth()];
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
+}
+
//
// item related methods (inherited from wxDataViewWidgetImpl)
//
void wxDataViewColumn::SetWidth(int width)
{
- [m_NativeDataPtr->GetNativeColumnPtr() setWidth:width];
m_width = width;
+
+ switch ( width )
+ {
+ case wxCOL_WIDTH_AUTOSIZE:
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
+ if ( GetOwner() )
+ {
+ wxCocoaDataViewControl *peer = static_cast<wxCocoaDataViewControl*>(GetOwner()->GetPeer());
+ peer->FitColumnWidthToContent(GetOwner()->GetColumnPosition(this));
+ break;
+ }
+#endif
+ // fall through if unsupported (OSX < 10.5) or not yet settable
+
+ case wxCOL_WIDTH_DEFAULT:
+ width = wxDVC_DEFAULT_WIDTH;
+ // fall through
+
+ default:
+ [m_NativeDataPtr->GetNativeColumnPtr() setWidth:width];
+ break;
+ }
}
void wxDataViewColumn::SetAsSortKey(bool WXUNUSED(sort))
void AdjustRowHeight(wxDataViewItem const& item);
// ... and the same method for a couple of items:
void AdjustRowHeights(wxDataViewItemArray const& items);
+ // adjust wxCOL_WIDTH_AUTOSIZE columns to fit the data
+ void AdjustAutosizedColumns();
private:
wxDataViewCtrl* m_DataViewCtrlPtr;
m_DataViewCtrlPtr->HandleWindowEvent(dataViewEvent);
// row height may have to be adjusted:
AdjustRowHeight(item);
+ AdjustAutosizedColumns();
// done
return true;
}
return false;
// if this location is reached all items have been updated:
AdjustRowHeights(items);
+ AdjustAutosizedColumns();
// done:
return true;
}
noFailureFlag = m_DataViewCtrlPtr->GetDataViewPeer()->Remove(parent,item);
// enable automatic updating again:
m_DataViewCtrlPtr->SetDeleting(false);
+
+ AdjustAutosizedColumns();
// done:
return noFailureFlag;
}
noFailureFlag = m_DataViewCtrlPtr->GetDataViewPeer()->Remove(parent,items);
// enable automatic updating again:
m_DataViewCtrlPtr->SetDeleting(false);
+
+ AdjustAutosizedColumns();
// done:
return noFailureFlag;
}
dataViewEvent.SetItem(item);
// send the equivalent wxWidget event:
m_DataViewCtrlPtr->HandleWindowEvent(dataViewEvent);
+
+ AdjustAutosizedColumns();
// done
return true;
}
}
}
+void wxOSXDataViewModelNotifier::AdjustAutosizedColumns()
+{
+ unsigned count = m_DataViewCtrlPtr->GetColumnCount();
+ for ( unsigned col = 0; col < count; col++ )
+ {
+ wxDataViewColumn *column = m_DataViewCtrlPtr->GetColumnPtr(col);
+
+ if ( column->GetWidthVariable() == wxCOL_WIDTH_AUTOSIZE )
+ m_DataViewCtrlPtr->GetDataViewPeer()->FitColumnWidthToContent(col);
+ }
+}
+
// ---------------------------------------------------------
// wxDataViewCustomRenderer
// The constructor, the implementation macro and environment