]> git.saurik.com Git - wxWidgets.git/blobdiff - src/osx/cocoa/dataview.mm
Applied Blit system options optimization to StretchBlit
[wxWidgets.git] / src / osx / cocoa / dataview.mm
index 29d412a26b0356296af38b2bbecce69e0073fe41..16ce4c8fabb03142ea900148dd98071644fe5e02 100644 (file)
@@ -24,6 +24,8 @@
 #include "wx/osx/private.h"
 #include "wx/osx/cocoa/dataview.h"
 #include "wx/renderer.h"
+#include "wx/stopwatch.h"
+#include "wx/dcgraph.h"
 
 // ============================================================================
 // Constants used locally
@@ -321,7 +323,6 @@ NSTableColumn* CreateNativeColumn(const wxDataViewColumn *column)
     );
 
     // setting the size related parameters:
-    const int width = column->GetWidthVariable();
     int resizingMask;
     if (column->IsResizeable())
     {
@@ -329,7 +330,7 @@ NSTableColumn* CreateNativeColumn(const wxDataViewColumn *column)
         [nativeColumn setMinWidth:column->GetMinWidth()];
         [nativeColumn setMaxWidth:column->GetMaxWidth()];
     }
-    else // column is not resizeable [by user]
+    else // column is not resizable [by user]
     {
         // if the control doesn't show a header, make the columns resize
         // automatically, this is particularly important for the single column
@@ -340,7 +341,6 @@ NSTableColumn* CreateNativeColumn(const wxDataViewColumn *column)
                             : NSTableColumnNoResizing;
     }
     [nativeColumn setResizingMask:resizingMask];
-    [nativeColumn setWidth:width];
 
 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
     // setting the visibility:
@@ -495,6 +495,9 @@ outlineView:(NSOutlineView*)outlineView
     acceptDrop:(id<NSDraggingInfo>)info
     item:(id)item childIndex:(NSInteger)index
 {
+    wxUnusedVar(outlineView);
+    wxUnusedVar(index);
+    
     NSArray* supportedTypes(
         [NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil]
     );
@@ -518,7 +521,7 @@ outlineView:(NSOutlineView*)outlineView
     event.SetItem(wxDataViewItemFromItem(item));
     event.SetModel(dvc->GetModel());
 
-    BOOL dragSuccessful;
+    BOOL dragSuccessful = false;
     if ( [bestType compare:DataViewPboardType] == NSOrderedSame )
     {
         NSArray* dataArray((NSArray*)
@@ -610,6 +613,8 @@ outlineView:(NSOutlineView*)outlineView
     child:(NSInteger)index
     ofItem:(id)item
 {
+    wxUnusedVar(outlineView);
+
     if ((item == currentParentItem) &&
             (index < ((NSInteger) [self getChildCount])))
         return [self getChild:index];
@@ -626,12 +631,16 @@ outlineView:(NSOutlineView*)outlineView
 
 -(BOOL) outlineView:(NSOutlineView*)outlineView isItemExpandable:(id)item
 {
+    wxUnusedVar(outlineView);
+
     wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
     return model->IsContainer(wxDataViewItemFromItem(item));
 }
 
 -(NSInteger) outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(id)item
 {
+    wxUnusedVar(outlineView);
+
     NSInteger noOfChildren;
 
     wxDataViewItemArray dataViewChildren;
@@ -651,6 +660,8 @@ outlineView:(NSOutlineView*)outlineView
     objectValueForTableColumn:(NSTableColumn*)tableColumn
     byItem:(id)item
 {
+    wxUnusedVar(outlineView);
+
     wxCHECK_MSG( model, nil, "Valid model in data source does not exist." );
 
     wxDataViewColumn* col(static_cast<wxDataViewColumn*>([[tableColumn identifier] pointer]));
@@ -674,6 +685,8 @@ outlineView:(NSOutlineView*)outlineView
     forTableColumn:(NSTableColumn*)tableColumn
     byItem:(id)item
 {
+    wxUnusedVar(outlineView);
+
     wxDataViewColumn* col(static_cast<wxDataViewColumn*>([[tableColumn identifier] pointer]));
 
     col->GetRenderer()->
@@ -682,6 +695,8 @@ outlineView:(NSOutlineView*)outlineView
 
 -(void) outlineView:(NSOutlineView*)outlineView sortDescriptorsDidChange:(NSArray*)oldDescriptors
 {
+    wxUnusedVar(oldDescriptors);
+    
     // Warning: the new sort descriptors are guaranteed to be only of type
     // NSSortDescriptor! Therefore, the sort descriptors for the data source
     // have to be converted.
@@ -731,6 +746,9 @@ outlineView:(NSOutlineView*)outlineView
 
 -(NSDragOperation) outlineView:(NSOutlineView*)outlineView validateDrop:(id<NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
 {
+    wxUnusedVar(outlineView);
+    wxUnusedVar(index);
+
     NSArray* supportedTypes([NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil]);
 
     NSPasteboard* pasteboard([info draggingPasteboard]);
@@ -739,7 +757,7 @@ outlineView:(NSOutlineView*)outlineView
     if (bestType == nil)
         return NSDragOperationNone;
 
-    NSDragOperation dragOperation;
+    NSDragOperation dragOperation = NSDragOperationNone;
     wxDataViewCtrl* const dvc(implementation->GetDataViewCtrl());
 
     wxCHECK_MSG(dvc, false, "Pointer to data view control not set correctly.");
@@ -828,6 +846,8 @@ outlineView:(NSOutlineView*)outlineView
 
 -(BOOL) outlineView:(NSOutlineView*)outlineView writeItems:(NSArray*)writeItems toPasteboard:(NSPasteboard*)pasteboard
 {
+    wxUnusedVar(outlineView);
+
     // the pasteboard will be filled up with an array containing the data as
     // returned by the events (including the data type) and a concatenation of
     // text (string) data; the text data will only be put onto the pasteboard
@@ -843,7 +863,7 @@ outlineView:(NSOutlineView*)outlineView
     if ([writeItems count] > 0)
     {
         bool            dataStringAvailable(true); // a flag indicating if for all items a data string is available
-        NSMutableArray* dataArray = [[NSMutableArray arrayWithCapacity:[writeItems count]] retain]; // data of all items
+        NSMutableArray* dataArray = [NSMutableArray arrayWithCapacity:[writeItems count]]; // data of all items
         wxString        dataString; // contains the string data of all items
 
         // send a begin drag event for all selected items and proceed with
@@ -1145,9 +1165,27 @@ outlineView:(NSOutlineView*)outlineView
 
     wxDataViewCustomRenderer * const renderer = obj->customRenderer;
 
-    wxDC * const dc = renderer->GetDC();
-    renderer->WXCallRender(wxFromNSRect(controlView, cellFrame), dc, 0);
-    renderer->SetDC(NULL);
+    // if this method is called everything is already setup correctly, 
+    CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
+    CGContextSaveGState( context );
+    
+    if ( ![controlView isFlipped] )
+    {
+        CGContextTranslateCTM( context, 0,  [controlView bounds].size.height );
+        CGContextScaleCTM( context, 1, -1 );
+    }
+        
+    wxGCDC dc;
+    wxGraphicsContext* gc = wxGraphicsContext::CreateFromNative(context);
+    dc.SetGraphicsContext(gc);
+
+    int state = 0;
+    if ( [self isHighlighted] )
+        state |= wxDATAVIEW_CELL_SELECTED;
+
+    renderer->WXCallRender(wxFromNSRect(controlView, cellFrame), &dc, state);
+
+    CGContextRestoreGState( context );
 }
 
 -(NSRect) imageRectForBounds:(NSRect)cellFrame
@@ -1535,6 +1573,8 @@ outlineView:(NSOutlineView*)outlineView
 //
 -(void) actionDoubleClick:(id)sender
 {
+    wxUnusedVar(sender);
+
     // actually the documentation (NSTableView 2007-10-31) for doubleAction:
     // and setDoubleAction: seems to be wrong as this action message is always
     // sent whether the cell is editable or not
@@ -1554,6 +1594,8 @@ outlineView:(NSOutlineView*)outlineView
 //
 -(NSMenu*) menuForEvent:(NSEvent*)theEvent
 {
+    wxUnusedVar(theEvent);
+
     // this method does not do any special menu event handling but only sends
     // an event message; therefore, the user has full control if a context
     // menu should be shown or not
@@ -1625,6 +1667,8 @@ outlineView:(NSOutlineView*)outlineView
 
 -(BOOL) outlineView:(NSOutlineView*)outlineView shouldCollapseItem:(id)item
 {
+    wxUnusedVar(outlineView);
+
     wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
     wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,dvc->GetId());
@@ -1641,6 +1685,8 @@ outlineView:(NSOutlineView*)outlineView
 
 -(BOOL) outlineView:(NSOutlineView*)outlineView shouldExpandItem:(id)item
 {
+    wxUnusedVar(outlineView);
+
     wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
     wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING,dvc->GetId());
@@ -1657,6 +1703,9 @@ outlineView:(NSOutlineView*)outlineView
 
 -(BOOL) outlineView:(NSOutlineView*)outlineView shouldSelectTableColumn:(NSTableColumn*)tableColumn
 {
+    wxUnusedVar(tableColumn);
+    wxUnusedVar(outlineView);
+
     return NO;
 }
 
@@ -1665,6 +1714,8 @@ outlineView:(NSOutlineView*)outlineView
     forTableColumn:(NSTableColumn*)tableColumn
     item:(id)item
 {
+    wxUnusedVar(outlineView);
+
     wxDataViewCtrl * const dvc = implementation->GetDataViewCtrl();
     wxDataViewModel * const model = dvc->GetModel();
 
@@ -1695,6 +1746,9 @@ outlineView:(NSOutlineView*)outlineView
     model->GetAttr(dvItem, colIdx, attr);
     renderer->OSXApplyAttr(attr);
 
+    // set the state (enabled/disabled) of the item
+    renderer->OSXApplyEnabled(model->IsEnabled(dvItem, colIdx));
+
     // and finally do draw it
     renderer->MacRender();
 }
@@ -1747,6 +1801,8 @@ outlineView:(NSOutlineView*)outlineView
 
 -(void) outlineViewSelectionDidChange:(NSNotification*)notification
 {
+    wxUnusedVar(notification);
+
     wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
 
     wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED,dvc->GetId());
@@ -1942,10 +1998,132 @@ bool wxCocoaDataViewControl::InsertColumn(unsigned int pos, wxDataViewColumn* co
     [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)
 //
@@ -2006,9 +2184,9 @@ bool wxCocoaDataViewControl::IsExpanded(const wxDataViewItem& item) const
 bool wxCocoaDataViewControl::Reload()
 {
     [m_DataSource clearBuffers];
+    [m_OutlineView reloadData];
     [m_OutlineView scrollColumnToVisible:0];
     [m_OutlineView scrollRowToVisible:0];
-    [m_OutlineView reloadData];
     return true;
 }
 
@@ -2032,6 +2210,8 @@ bool wxCocoaDataViewControl::Remove(const wxDataViewItem& parent, const wxDataVi
 
 bool wxCocoaDataViewControl::Update(const wxDataViewColumn *columnPtr)
 {
+    wxUnusedVar(columnPtr);
+    
     return false;
 }
 
@@ -2085,6 +2265,11 @@ void wxCocoaDataViewControl::SetCurrentItem(const wxDataViewItem& item)
     Select(item);
 }
 
+int wxCocoaDataViewControl::GetSelectedItemsCount() const
+{
+    return [m_OutlineView numberOfSelectedRows];
+}
+
 int wxCocoaDataViewControl::GetSelections(wxDataViewItemArray& sel) const
 {
     NSIndexSet* selectedRowIndexes([m_OutlineView selectedRowIndexes]);
@@ -2153,6 +2338,11 @@ void wxCocoaDataViewControl::Resort()
     [m_OutlineView reloadData];
 }
 
+void wxCocoaDataViewControl::StartEditor( const wxDataViewItem & item, unsigned int column )
+{
+    [m_OutlineView editColumn:column row:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]] withEvent:nil select:YES];
+}
+
 //
 // other methods (inherited from wxDataViewWidgetImpl)
 //
@@ -2487,6 +2677,11 @@ void wxDataViewRenderer::OSXApplyAttr(const wxDataViewItemAttr& attr)
         [(id)cell setTextColor:colText];
 }
 
+void wxDataViewRenderer::OSXApplyEnabled(bool enabled)
+{
+    [GetNativeData()->GetItemCell() setEnabled:enabled];
+}
+
 IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer,wxDataViewRendererBase)
 
 // ---------------------------------------------------------
@@ -2614,9 +2809,9 @@ wxDataViewChoiceRenderer::wxDataViewChoiceRenderer(const wxArrayString& choices,
 
     cell = [[NSPopUpButtonCell alloc] init];
     [cell setControlSize:NSMiniControlSize];
-    [cell setFont:[[NSFont fontWithName:[[cell font] fontName] size:[NSFont systemFontSizeForControlSize:NSMiniControlSize]] autorelease]];
+    [cell setFont:[NSFont fontWithName:[[cell font] fontName] size:[NSFont systemFontSizeForControlSize:NSMiniControlSize]]];
     for (size_t i=0; i<choices.GetCount(); ++i)
-        [cell addItemWithTitle:[[wxCFStringRef(choices[i]).AsNSString() retain] autorelease]];
+        [cell addItemWithTitle:wxCFStringRef(choices[i]).AsNSString()];
     SetNativeData(new wxDataViewRendererNativeData(cell));
     [cell release];
 }
@@ -2837,6 +3032,8 @@ wxDataViewProgressRenderer::wxDataViewProgressRenderer(const wxString& label,
                                                        int align)
     : wxDataViewRenderer(varianttype,mode,align)
 {
+    wxUnusedVar(label);
+    
     NSLevelIndicatorCell* cell;
 
     cell = [[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle];
@@ -2886,7 +3083,8 @@ wxDataViewColumn::wxDataViewColumn(const wxString& title,
        m_title(title)
 {
     InitCommon(width, align, flags);
-    if (renderer && (renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT))
+    if (renderer && !renderer->IsCustomRenderer() &&
+        (renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT))
         renderer->SetAlignment(align);
 }
 
@@ -2900,7 +3098,8 @@ wxDataViewColumn::wxDataViewColumn(const wxBitmap& bitmap,
       m_NativeDataPtr(new wxDataViewColumnNativeData())
 {
     InitCommon(width, align, flags);
-    if (renderer && (renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT))
+    if (renderer && !renderer->IsCustomRenderer() &&
+        (renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT))
         renderer->SetAlignment(align);
 }
 
@@ -2924,7 +3123,8 @@ void wxDataViewColumn::SetAlignment(wxAlignment align)
 {
     m_alignment = align;
     [[m_NativeDataPtr->GetNativeColumnPtr() headerCell] setAlignment:ConvertToNativeHorizontalTextAlignment(align)];
-    if (m_renderer && (m_renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT))
+    if (m_renderer && !m_renderer->IsCustomRenderer() &&
+        (m_renderer->GetAlignment() == wxDVR_DEFAULT_ALIGNMENT))
         m_renderer->SetAlignment(align);
 }
 
@@ -2951,6 +3151,7 @@ void wxDataViewColumn::SetMinWidth(int minWidth)
 
 void wxDataViewColumn::SetReorderable(bool reorderable)
 {
+    wxUnusedVar(reorderable);
 }
 
 void wxDataViewColumn::SetHidden(bool hidden)
@@ -2965,10 +3166,10 @@ bool wxDataViewColumn::IsHidden() const
     return [m_NativeDataPtr->GetNativeColumnPtr() isHidden];
 }
 
-void wxDataViewColumn::SetResizeable(bool resizeable)
+void wxDataViewColumn::SetResizeable(bool resizable)
 {
-    wxDataViewColumnBase::SetResizeable(resizeable);
-    if (resizeable)
+    wxDataViewColumnBase::SetResizeable(resizable);
+    if (resizable)
         [m_NativeDataPtr->GetNativeColumnPtr() setResizingMask:NSTableColumnUserResizingMask];
     else
         [m_NativeDataPtr->GetNativeColumnPtr() setResizingMask:NSTableColumnNoResizing];
@@ -3017,14 +3218,29 @@ void wxDataViewColumn::SetTitle(const wxString& title)
 
 void wxDataViewColumn::SetWidth(int width)
 {
-    [m_NativeDataPtr->GetNativeColumnPtr() setWidth:width];
     m_width = width;
-}
 
-void wxDataViewColumn::SetAsSortKey(bool WXUNUSED(sort))
-{
-    // see wxGTK native wxDataViewColumn implementation
-    wxFAIL_MSG("not implemented");
+    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::SetNativeData(wxDataViewColumnNativeData* newNativeDataPtr)