X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/f49f59d0c9375f0a183c83b971ac81ce385dfdf9..0afeb753e0a6a3fdba290bf3612bb2f012d44d95:/src/osx/cocoa/dataview.mm?ds=sidebyside diff --git a/src/osx/cocoa/dataview.mm b/src/osx/cocoa/dataview.mm index 3a244c6829..465305fbbc 100644 --- a/src/osx/cocoa/dataview.mm +++ b/src/osx/cocoa/dataview.mm @@ -21,9 +21,11 @@ #include "wx/utils.h" #endif -#include "wx/osx/cocoa/dataview.h" #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 @@ -154,11 +156,46 @@ inline wxDataViewItem wxDataViewItemFromMaybeNilItem(id item) { } + // Get the identifier we use for the specified column. This should be used + // for finding columns from identifier only, to initialize the identifier + // of a new column use initWithColumnPointer below instead. + +(NSString*) identifierForColumnPointer:(const wxDataViewColumn*)column; + + // Initialize the column with the given pointer to the associated + // wxDataViewColumn. This pointer can later be retrieved using + // getColumnPointer. + -(id) initWithColumnPointer:(const wxDataViewColumn*)column; + + // Retrieve the associated column. + -(wxDataViewColumn*) getColumnPointer; + -(id) dataCellForRow:(NSInteger)row; @end @implementation wxDVCNSTableColumn ++(NSString*) identifierForColumnPointer:(const wxDataViewColumn*)column +{ + // Starting from OS X 10.7 the column identifier must be an NSString and + // not just some arbitrary object, so we serialize the pointer into the + // string. Notice the use of NSInteger which is big enough to store a + // pointer in both 32 and 64 bit builds. + return [NSString stringWithFormat:@"%lu", reinterpret_cast(column)]; +} + +-(id) initWithColumnPointer:(const wxDataViewColumn*)column +{ + [self initWithIdentifier: [wxDVCNSTableColumn identifierForColumnPointer:column]]; + return self; +} + +-(wxDataViewColumn*) getColumnPointer +{ + // The case to NSString is needed for OS X < 10.7. + return reinterpret_cast( + [static_cast([self identifier]) integerValue]); +} + -(id) dataCellForRow:(NSInteger)row { // what we want to do here is to simply return nil for the cells which @@ -166,13 +203,7 @@ inline wxDataViewItem wxDataViewItemFromMaybeNilItem(id item) // or progress cells in the columns using the corresponding types even for // the container rows which is wrong - // half of the problem is just finding the objects we need from the column - // pointer which is itself stashed inside wxPointerObject which we use as - // our identifier - const wxDataViewColumn * const - dvCol = static_cast( - [(wxPointerObject *)[self identifier] pointer] - ); + const wxDataViewColumn * const dvCol = [self getColumnPointer]; const wxDataViewCtrl * const dvc = dvCol->GetOwner(); const wxCocoaDataViewControl * const @@ -314,14 +345,10 @@ NSTableColumn* CreateNativeColumn(const wxDataViewColumn *column) wxCHECK_MSG( renderer, NULL, "column should have a renderer" ); wxDVCNSTableColumn * const nativeColumn( - [[wxDVCNSTableColumn alloc] initWithIdentifier: - [[[wxPointerObject alloc] initWithPointer: - const_cast(column)] - autorelease]] + [[wxDVCNSTableColumn alloc] initWithColumnPointer: column] ); // setting the size related parameters: - const int width = column->GetWidthVariable(); int resizingMask; if (column->IsResizeable()) { @@ -329,7 +356,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 +367,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 +521,9 @@ outlineView:(NSOutlineView*)outlineView acceptDrop:(id)info item:(id)item childIndex:(NSInteger)index { + wxUnusedVar(outlineView); + wxUnusedVar(index); + NSArray* supportedTypes( [NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil] ); @@ -513,12 +542,12 @@ outlineView:(NSOutlineView*)outlineView wxCHECK_MSG( dvc->GetModel(), false, "Pointer to model not set correctly." ); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_DROP, dvc->GetId()); + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_DROP, dvc->GetId()); event.SetEventObject(dvc); event.SetItem(wxDataViewItemFromItem(item)); event.SetModel(dvc->GetModel()); - BOOL dragSuccessful; + BOOL dragSuccessful = false; if ( [bestType compare:DataViewPboardType] == NSOrderedSame ) { NSArray* dataArray((NSArray*) @@ -603,12 +632,15 @@ outlineView:(NSOutlineView*)outlineView ::CFRelease(osxData); delete dataObjects; } + return dragSuccessful; } -(id) outlineView:(NSOutlineView*)outlineView child:(NSInteger)index ofItem:(id)item { + wxUnusedVar(outlineView); + if ((item == currentParentItem) && (index < ((NSInteger) [self getChildCount]))) return [self getChild:index]; @@ -625,12 +657,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; @@ -650,9 +686,12 @@ 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([[tableColumn identifier] pointer])); + wxDataViewColumn* const + col([static_cast(tableColumn) getColumnPointer]); const unsigned colIdx = col->GetModelColumn(); wxDataViewItem dataViewItem(wxDataViewItemFromItem(item)); @@ -673,7 +712,10 @@ outlineView:(NSOutlineView*)outlineView forTableColumn:(NSTableColumn*)tableColumn byItem:(id)item { - wxDataViewColumn* col(static_cast([[tableColumn identifier] pointer])); + wxUnusedVar(outlineView); + + wxDataViewColumn* const + col([static_cast(tableColumn) getColumnPointer]); col->GetRenderer()-> OSXOnCellChanged(object, wxDataViewItemFromItem(item), col->GetModelColumn()); @@ -681,6 +723,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. @@ -705,11 +749,11 @@ outlineView:(NSOutlineView*)outlineView sortingColumnPtr:dvc->GetColumn([[newDescriptor key] intValue]) ascending:[newDescriptor ascending]] autorelease]]; } - [[outlineView dataSource] setSortDescriptors:wxSortDescriptors]; + [(wxCocoaOutlineDataSource*)[outlineView dataSource] setSortDescriptors:wxSortDescriptors]; // send first the event to wxWidgets that the sorting has changed so that // the program can do special actions before the sorting actually starts: - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_COLUMN_SORTED,dvc->GetId()); // variable defintion + wxDataViewEvent event(wxEVT_DATAVIEW_COLUMN_SORTED,dvc->GetId()); // variable definition event.SetEventObject(dvc); if (noOfDescriptors > 0) @@ -730,6 +774,9 @@ outlineView:(NSOutlineView*)outlineView -(NSDragOperation) outlineView:(NSOutlineView*)outlineView validateDrop:(id)info proposedItem:(id)item proposedChildIndex:(NSInteger)index { + wxUnusedVar(outlineView); + wxUnusedVar(index); + NSArray* supportedTypes([NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil]); NSPasteboard* pasteboard([info draggingPasteboard]); @@ -738,14 +785,14 @@ 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."); wxCHECK_MSG(dvc->GetModel(), false, "Pointer to model not set correctly."); wxDataViewEvent - event(wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE,dvc->GetId()); + event(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE,dvc->GetId()); event.SetEventObject(dvc); event.SetItem(wxDataViewItemFromItem(item)); @@ -827,6 +874,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 @@ -842,13 +891,13 @@ 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 // dragging unless the event is vetoed: wxDataViewEvent - event(wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG,dvc->GetId()); + event(wxEVT_DATAVIEW_ITEM_BEGIN_DRAG,dvc->GetId()); event.SetEventObject(dvc); event.SetModel(dvc->GetModel()); @@ -877,7 +926,8 @@ outlineView:(NSOutlineView*)outlineView size_t const dataSize = event.GetDataObject()->GetDataSize(idDataFormat); size_t const dataBufferSize = sizeof(wxDataFormatId)+dataSize; // variable definitions (used in all case statements): - wxMemoryBuffer dataBuffer(dataBufferSize); + // give additional headroom for trailing NULL + wxMemoryBuffer dataBuffer(dataBufferSize+4); dataBuffer.AppendData(&idDataFormat,sizeof(wxDataFormatId)); switch (idDataFormat) @@ -909,7 +959,6 @@ outlineView:(NSOutlineView*)outlineView break; default: wxFAIL_MSG("Data object has invalid or unsupported data format"); - [dataArray release]; return NO; } } @@ -927,7 +976,6 @@ outlineView:(NSOutlineView*)outlineView } else { - [dataArray release]; delete itemObject; return NO; // dragging was vetoed or no data available } @@ -1118,6 +1166,23 @@ outlineView:(NSOutlineView*)outlineView @implementation wxCustomCell +#if 0 // starting implementation for custom cell clicks + +- (id)init +{ + self = [super init]; + [self setAction:@selector(clickedAction)]; + [self setTarget:self]; + return self; +} + +- (void) clickedAction: (id) sender +{ + wxUnusedVar(sender); +} + +#endif + -(NSSize) cellSize { wxCustomRendererObject * const @@ -1144,9 +1209,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 @@ -1534,12 +1617,14 @@ 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 wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED,dvc->GetId()); + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_ACTIVATED,dvc->GetId()); event.SetEventObject(dvc); @@ -1553,12 +1638,14 @@ 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 wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU,dvc->GetId()); + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU,dvc->GetId()); wxDataViewItemArray selectedItems; @@ -1581,12 +1668,13 @@ outlineView:(NSOutlineView*)outlineView // -(void) outlineView:(NSOutlineView*)outlineView mouseDownInHeaderOfTableColumn:(NSTableColumn*)tableColumn { - wxDataViewColumn* const col(static_cast([[tableColumn identifier] pointer])); + wxDataViewColumn* const + col([static_cast(tableColumn) getColumnPointer]); wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); wxDataViewEvent - event(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK,dvc->GetId()); + event(wxEVT_DATAVIEW_COLUMN_HEADER_CLICK,dvc->GetId()); // first, send an event that the user clicked into a column's header: @@ -1624,9 +1712,11 @@ 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()); + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_COLLAPSING,dvc->GetId()); event.SetEventObject(dvc); @@ -1640,9 +1730,11 @@ 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()); + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_EXPANDING,dvc->GetId()); event.SetEventObject(dvc); @@ -1656,6 +1748,9 @@ outlineView:(NSOutlineView*)outlineView -(BOOL) outlineView:(NSOutlineView*)outlineView shouldSelectTableColumn:(NSTableColumn*)tableColumn { + wxUnusedVar(tableColumn); + wxUnusedVar(outlineView); + return NO; } @@ -1664,14 +1759,13 @@ outlineView:(NSOutlineView*)outlineView forTableColumn:(NSTableColumn*)tableColumn item:(id)item { + wxUnusedVar(outlineView); + wxDataViewCtrl * const dvc = implementation->GetDataViewCtrl(); wxDataViewModel * const model = dvc->GetModel(); - wxDataViewColumn * const - dvCol(static_cast( - [[tableColumn identifier] pointer] - ) - ); + wxDataViewColumn* const + dvCol([static_cast(tableColumn) getColumnPointer]); const unsigned colIdx = dvCol->GetModelColumn(); wxDataViewItem dvItem(wxDataViewItemFromItem(item)); @@ -1694,6 +1788,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(); } @@ -1705,11 +1802,14 @@ outlineView:(NSOutlineView*)outlineView { int const newColumnPosition = [[[notification userInfo] objectForKey:@"NSNewColumn"] intValue]; - wxDataViewColumn* const col(static_cast([[[[self tableColumns] objectAtIndex:newColumnPosition] identifier] pointer])); + NSTableColumn* + tableColumn = [[self tableColumns] objectAtIndex:newColumnPosition]; + wxDataViewColumn* const + col([static_cast(tableColumn) getColumnPointer]); wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_COLUMN_REORDERED,dvc->GetId()); + wxDataViewEvent event(wxEVT_DATAVIEW_COLUMN_REORDERED,dvc->GetId()); event.SetEventObject(dvc); @@ -1722,7 +1822,7 @@ outlineView:(NSOutlineView*)outlineView { wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSED,dvc->GetId()); + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_COLLAPSED,dvc->GetId()); event.SetEventObject(dvc); @@ -1735,7 +1835,7 @@ outlineView:(NSOutlineView*)outlineView { wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDED,dvc->GetId()); + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_EXPANDED,dvc->GetId()); event.SetEventObject(dvc); @@ -1746,9 +1846,11 @@ outlineView:(NSOutlineView*)outlineView -(void) outlineViewSelectionDidChange:(NSNotification*)notification { + wxUnusedVar(notification); + wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); - wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED,dvc->GetId()); + wxDataViewEvent event(wxEVT_DATAVIEW_SELECTION_CHANGED,dvc->GetId()); event.SetEventObject(dvc); event.SetModel(dvc->GetModel()); @@ -1770,9 +1872,10 @@ outlineView:(NSOutlineView*)outlineView currentlyEditedColumn = [self editedColumn]; currentlyEditedRow = [self editedRow]; - wxDataViewColumn* const col = - static_cast( - [[[[self tableColumns] objectAtIndex:currentlyEditedColumn] identifier] pointer]); + NSTableColumn* + tableColumn = [[self tableColumns] objectAtIndex:currentlyEditedColumn]; + wxDataViewColumn* const + col([static_cast(tableColumn) getColumnPointer]); wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); @@ -1782,7 +1885,7 @@ outlineView:(NSOutlineView*)outlineView // now, send the event: wxDataViewEvent - event(wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_STARTED,dvc->GetId()); + event(wxEVT_DATAVIEW_ITEM_EDITING_STARTED,dvc->GetId()); event.SetEventObject(dvc); event.SetItem( @@ -1802,21 +1905,22 @@ outlineView:(NSOutlineView*)outlineView // even if no event indicating a start of an editing session has been sent // (see Documentation for NSControl controlTextDidEndEditing:); this is // not expected by a user of the wxWidgets library and therefore an - // wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_DONE event is only sent if a - // corresponding wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_STARTED has been sent - // before; to check if a wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_STARTED has + // wxEVT_DATAVIEW_ITEM_EDITING_DONE event is only sent if a + // corresponding wxEVT_DATAVIEW_ITEM_EDITING_STARTED has been sent + // before; to check if a wxEVT_DATAVIEW_ITEM_EDITING_STARTED has // been sent the last edited column/row are valid: if ( currentlyEditedColumn != -1 && currentlyEditedRow != -1 ) { - wxDataViewColumn* const col = - static_cast( - [[[[self tableColumns] objectAtIndex:currentlyEditedColumn] identifier] pointer]); + NSTableColumn* + tableColumn = [[self tableColumns] objectAtIndex:currentlyEditedColumn]; + wxDataViewColumn* const + col([static_cast(tableColumn) getColumnPointer]); wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); // send event to wxWidgets: wxDataViewEvent - event(wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_DONE,dvc->GetId()); + event(wxEVT_DATAVIEW_ITEM_EDITING_DONE,dvc->GetId()); event.SetEventObject(dvc); event.SetItem( @@ -1860,6 +1964,9 @@ wxCocoaDataViewControl::wxCocoaDataViewControl(wxWindow* peer, [scrollview setAutohidesScrollers:YES]; [scrollview setDocumentView:m_OutlineView]; + // we cannot call InstallHandler(m_OutlineView) here, because we are handling + // our action:s ourselves, only associate the view with this impl + Associate(m_OutlineView,this); // initialize the native control itself too InitOutlineView(style); } @@ -1912,7 +2019,7 @@ bool wxCocoaDataViewControl::DeleteColumn(wxDataViewColumn* columnPtr) [m_OutlineView setOutlineTableColumn:nil]; // due to a bug this does not work [m_OutlineView removeTableColumn:columnPtr->GetNativeData()->GetNativeColumnPtr()]; // due to a confirmed bug #6555162 the deletion does not work for // outline table columns (... and there is no workaround) - return (([m_OutlineView columnWithIdentifier:[[[wxPointerObject alloc] initWithPointer:columnPtr] autorelease]]) == -1); + return (([m_OutlineView columnWithIdentifier:[wxDVCNSTableColumn identifierForColumnPointer:columnPtr]]) == -1); } void wxCocoaDataViewControl::DoSetExpanderColumn(const wxDataViewColumn *columnPtr) @@ -1922,12 +2029,13 @@ void wxCocoaDataViewControl::DoSetExpanderColumn(const wxDataViewColumn *columnP wxDataViewColumn* wxCocoaDataViewControl::GetColumn(unsigned int pos) const { - return static_cast([[[[m_OutlineView tableColumns] objectAtIndex:pos] identifier] pointer]); + NSTableColumn* tableColumn = [[m_OutlineView tableColumns] objectAtIndex:pos]; + return [static_cast(tableColumn) getColumnPointer]; } int wxCocoaDataViewControl::GetColumnPosition(const wxDataViewColumn *columnPtr) const { - return [m_OutlineView columnWithIdentifier:[[[wxPointerObject alloc] initWithPointer:const_cast(columnPtr)] autorelease]]; + return [m_OutlineView columnWithIdentifier:[wxDVCNSTableColumn identifierForColumnPointer:columnPtr]]; } bool wxCocoaDataViewControl::InsertColumn(unsigned int pos, wxDataViewColumn* columnPtr) @@ -1941,10 +2049,132 @@ bool wxCocoaDataViewControl::InsertColumn(unsigned int pos, wxDataViewColumn* co [m_OutlineView addTableColumn:nativeColumn]; if (pos != static_cast([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) // @@ -2005,9 +2235,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; } @@ -2031,6 +2261,8 @@ bool wxCocoaDataViewControl::Remove(const wxDataViewItem& parent, const wxDataVi bool wxCocoaDataViewControl::Update(const wxDataViewColumn *columnPtr) { + wxUnusedVar(columnPtr); + return false; } @@ -2068,6 +2300,35 @@ bool wxCocoaDataViewControl::AssociateModel(wxDataViewModel* model) // // selection related methods (inherited from wxDataViewWidgetImpl) // + +wxDataViewItem wxCocoaDataViewControl::GetCurrentItem() const +{ + return wxDataViewItem([[m_OutlineView itemAtRow:[m_OutlineView selectedRow]] pointer]); +} + +wxDataViewColumn *wxCocoaDataViewControl::GetCurrentColumn() const +{ + int col = [m_OutlineView selectedColumn]; + if ( col == -1 ) + return NULL; + return GetColumn(col); +} + +void wxCocoaDataViewControl::SetCurrentItem(const wxDataViewItem& item) +{ + // We can't have unselected current item in a NSTableView, as the + // documentation of its deselectRow method explains, the control will + // automatically change the current item to the closest still selected item + // if the current item is deselected. So we have no choice but to select + // the item in the same time as making it current. + Select(item); +} + +int wxCocoaDataViewControl::GetSelectedItemsCount() const +{ + return [m_OutlineView numberOfSelectedRows]; +} + int wxCocoaDataViewControl::GetSelections(wxDataViewItemArray& sel) const { NSIndexSet* selectedRowIndexes([m_OutlineView selectedRowIndexes]); @@ -2095,7 +2356,7 @@ void wxCocoaDataViewControl::Select(const wxDataViewItem& item) { if (item.IsOk()) [m_OutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[m_OutlineView rowForItem:[m_DataSource getDataViewItemFromBuffer:item]]] - byExtendingSelection:NO]; + byExtendingSelection:GetDataViewCtrl()->HasFlag(wxDV_MULTIPLE) ? YES : NO]; } void wxCocoaDataViewControl::SelectAll() @@ -2126,7 +2387,7 @@ wxDataViewColumn* wxCocoaDataViewControl::GetSortingColumn() const for (UInt32 i=0; i([[[columns objectAtIndex:i] identifier] pointer]); + return GetColumn(i); return NULL; } @@ -2136,6 +2397,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) // @@ -2156,7 +2422,7 @@ void wxCocoaDataViewControl::HitTest(const wxPoint& point, wxDataViewItem& item, indexRow = [m_OutlineView rowAtPoint: nativePoint]; if ((indexColumn >= 0) && (indexRow >= 0)) { - columnPtr = static_cast([[[[m_OutlineView tableColumns] objectAtIndex:indexColumn] identifier] pointer]); + columnPtr = GetColumn(indexColumn); item = wxDataViewItem([[m_OutlineView itemAtRow:indexRow] pointer]); } else @@ -2373,12 +2639,11 @@ wxDataViewRenderer::OSXOnCellChanged(NSObject *object, const wxDataViewItem& item, unsigned col) { - // TODO: we probably should get rid of this code entirely and make this - // function pure virtual, but currently we still have some native - // renderers (wxDataViewChoiceRenderer) which don't override it and - // there is also wxDataViewCustomRenderer for which it's not obvious - // how it should be implemented so keep this "auto-deduction" of - // variant type from NSObject for now + // TODO: This code should really be removed and this function be made pure + // virtual. We just need to decide what to do with custom renderers + // (i.e. wxDataViewCustomRenderer), currently OS X "in place" editing + // which doesn't really create an editor control is not compatible + // with the in place editing under other platforms. wxVariant value; if ( [object isKindOfClass:[NSString class]] ) @@ -2451,7 +2716,7 @@ void wxDataViewRenderer::OSXApplyAttr(const wxDataViewItemAttr& attr) } const wxColour& c = attr.GetColour(); - colText = [NSColor colorWithDeviceRed:c.Red() / 255. + colText = [NSColor colorWithCalibratedRed:c.Red() / 255. green:c.Green() / 255. blue:c.Blue() / 255. alpha:c.Alpha() / 255.]; @@ -2470,6 +2735,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) // --------------------------------------------------------- @@ -2597,13 +2867,24 @@ 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; iGetOwner()->GetModel(); + model->ChangeValue(GetChoice(ObjectToLong(value)), item, col); +} + bool wxDataViewChoiceRenderer::MacRender() { if (GetValue().GetType() == GetVariantType()) @@ -2743,6 +3024,8 @@ bool wxDataViewIconTextRenderer::MacRender() iconText << GetValue(); if (iconText.GetIcon().IsOk()) [cell setImage:[[wxBitmap(iconText.GetIcon()).GetNSImage() retain] autorelease]]; + else + [cell setImage:nil]; [cell setStringValue:[[wxCFStringRef(iconText.GetText()).AsNSString() retain] autorelease]]; return true; } @@ -2820,6 +3103,8 @@ wxDataViewProgressRenderer::wxDataViewProgressRenderer(const wxString& label, int align) : wxDataViewRenderer(varianttype,mode,align) { + wxUnusedVar(label); + NSLevelIndicatorCell* cell; cell = [[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle]; @@ -2869,7 +3154,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); } @@ -2883,7 +3169,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); } @@ -2907,7 +3194,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); } @@ -2934,12 +3222,25 @@ void wxDataViewColumn::SetMinWidth(int minWidth) void wxDataViewColumn::SetReorderable(bool reorderable) { + wxUnusedVar(reorderable); +} + +void wxDataViewColumn::SetHidden(bool hidden) +{ + // How to set flag here? + + [m_NativeDataPtr->GetNativeColumnPtr() setHidden:hidden]; } -void wxDataViewColumn::SetResizeable(bool resizeable) +bool wxDataViewColumn::IsHidden() const { - wxDataViewColumnBase::SetResizeable(resizeable); - if (resizeable) + return [m_NativeDataPtr->GetNativeColumnPtr() isHidden]; +} + +void wxDataViewColumn::SetResizeable(bool resizable) +{ + wxDataViewColumnBase::SetResizeable(resizable); + if (resizable) [m_NativeDataPtr->GetNativeColumnPtr() setResizingMask:NSTableColumnUserResizingMask]; else [m_NativeDataPtr->GetNativeColumnPtr() setResizingMask:NSTableColumnNoResizing]; @@ -2947,7 +3248,12 @@ void wxDataViewColumn::SetResizeable(bool resizeable) void wxDataViewColumn::SetSortable(bool sortable) { - wxDataViewColumnBase::SetSortable(sortable); + // wxDataViewColumnBase::SetSortable(sortable); + // Avoid endless recursion and just set the flag here + if (sortable) + m_flags |= wxDATAVIEW_COL_SORTABLE; + else + m_flags &= ~wxDATAVIEW_COL_SORTABLE; } void wxDataViewColumn::SetSortOrder(bool ascending) @@ -2983,14 +3289,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(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)