#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"
// ============================================================================
// Constants used locally
// ============================================================================
// Classes used locally in dataview.mm
// ============================================================================
+
+// ============================================================================
+// wxPointerObject
+// ============================================================================
+
+@implementation wxPointerObject
+
+-(id) init
+{
+ self = [super init];
+ if (self != nil)
+ self->pointer = NULL;
+ return self;
+}
+
+-(id) initWithPointer:(void*) initPointer
+{
+ self = [super init];
+ if (self != nil)
+ self->pointer = initPointer;
+ return self;
+}
+
+//
+// inherited methods from NSObject
+//
+-(BOOL) isEqual:(id)object
+{
+ return (object != nil) &&
+ ([object isKindOfClass:[wxPointerObject class]]) &&
+ (pointer == [((wxPointerObject*) object) pointer]);
+}
+
+-(NSUInteger) hash
+{
+ return (NSUInteger) pointer;
+}
+
+-(void*) pointer
+{
+ return pointer;
+}
+
+-(void) setPointer:(void*) newPointer
+{
+ pointer = newPointer;
+}
+
+@end
+
+namespace
+{
+
+inline wxDataViewItem wxDataViewItemFromItem(id item)
+{
+ return wxDataViewItem([static_cast<wxPointerObject *>(item) pointer]);
+}
+
+inline wxDataViewItem wxDataViewItemFromMaybeNilItem(id item)
+{
+ return item == nil ? wxDataViewItem() : wxDataViewItemFromItem(item);
+}
+
+} // anonymous namespace
+
+// ----------------------------------------------------------------------------
+// wxCustomRendererObject
+// ----------------------------------------------------------------------------
+
@interface wxCustomRendererObject : NSObject <NSCopying>
{
@public
}
@end
+// ----------------------------------------------------------------------------
+// wxDVCNSTableColumn: exists only to override NSTableColumn:dataCellForRow:
+// ----------------------------------------------------------------------------
+
+@interface wxDVCNSTableColumn : NSTableColumn
+{
+}
+
+ -(id) dataCellForRow:(NSInteger)row;
+@end
+
+@implementation wxDVCNSTableColumn
+
+-(id) dataCellForRow:(NSInteger)row
+{
+ // what we want to do here is to simply return nil for the cells which
+ // shouldn't show anything as otherwise we would show e.g. empty combo box
+ // 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<wxDataViewColumn *>(
+ [(wxPointerObject *)[self identifier] pointer]
+ );
+
+ const wxDataViewCtrl * const dvc = dvCol->GetOwner();
+ const wxCocoaDataViewControl * const
+ peer = static_cast<wxCocoaDataViewControl *>(dvc->GetPeer());
+
+
+ // once we do have everything, simply ask NSOutlineView for the item...
+ const id item = peer->GetItemAtRow(row);
+ if ( item )
+ {
+ // ... and if it succeeded, ask the model whether it has any value
+ wxDataViewItem dvItem(wxDataViewItemFromItem(item));
+
+ if ( !dvc->GetModel()->HasValue(dvItem, dvCol->GetModelColumn()) )
+ return nil;
+ }
+
+ return [super dataCellForRow:row];
+}
+
+@end
+
// ============================================================================
// local helpers
// ============================================================================
namespace
{
+// convert from NSObject to different C++ types: all these functions check
+// that the conversion really makes sense and assert if it doesn't
+wxString ObjectToString(NSObject *object)
+{
+ wxCHECK_MSG( [object isKindOfClass:[NSString class]], "",
+ wxString::Format
+ (
+ "string expected but got %s",
+ wxCFStringRef::AsString([object className])
+ ));
+
+ return wxCFStringRef([((NSString*) object) retain]).AsString();
+}
+
+bool ObjectToBool(NSObject *object)
+{
+ // actually the value must be of NSCFBoolean class but it's private so we
+ // can't check for it directly
+ wxCHECK_MSG( [object isKindOfClass:[NSNumber class]], false,
+ wxString::Format
+ (
+ "number expected but got %s",
+ wxCFStringRef::AsString([object className])
+ ));
+
+ return [(NSNumber *)object boolValue];
+}
+
+long ObjectToLong(NSObject *object)
+{
+ wxCHECK_MSG( [object isKindOfClass:[NSNumber class]], -1,
+ wxString::Format
+ (
+ "number expected but got %s",
+ wxCFStringRef::AsString([object className])
+ ));
+
+ return [(NSNumber *)object longValue];
+}
+
+wxDateTime ObjectToDate(NSObject *object)
+{
+ wxCHECK_MSG( [object isKindOfClass:[NSDate class]], wxInvalidDateTime,
+ wxString::Format
+ (
+ "date expected but got %s",
+ wxCFStringRef::AsString([object className])
+ ));
+
+ // get the number of seconds since 1970-01-01 UTC and this is the only
+ // way to convert a double to a wxLongLong
+ const wxLongLong seconds = [((NSDate*) object) timeIntervalSince1970];
+
+ wxDateTime dt(1, wxDateTime::Jan, 1970);
+ dt.Add(wxTimeSpan(0,0,seconds));
+
+ // the user has entered a date in the local timezone but seconds
+ // contains the number of seconds from date in the local timezone
+ // since 1970-01-01 UTC; therefore, the timezone information has to be
+ // transferred to wxWidgets, too:
+ dt.MakeFromTimezone(wxDateTime::UTC);
+
+ return dt;
+}
+
NSInteger CompareItems(id item1, id item2, void* context)
{
NSArray* const sortDescriptors = (NSArray*) context;
NSInteger result = NSOrderedSame;
for ( NSUInteger i = 0; i < count && result == NSOrderedSame; ++i )
{
- // constant definition for abbreviational purposes:
wxSortDescriptorObject* const
sortDescriptor = (wxSortDescriptorObject*)
[sortDescriptors objectAtIndex:i];
int rc = [sortDescriptor modelPtr]->Compare
(
- wxDataViewItem([((wxPointerObject*) item1) pointer]),
- wxDataViewItem([((wxPointerObject*) item2) pointer]),
+ wxDataViewItemFromItem(item1),
+ wxDataViewItemFromItem(item2),
[sortDescriptor columnPtr]->GetModelColumn(),
[sortDescriptor ascending] == YES
);
wxCHECK_MSG( renderer, NULL, "column should have a renderer" );
- NSTableColumn * const nativeColumn(
- [[NSTableColumn alloc] initWithIdentifier:
+ wxDVCNSTableColumn * const nativeColumn(
+ [[wxDVCNSTableColumn alloc] initWithIdentifier:
[[[wxPointerObject alloc] initWithPointer:
const_cast<wxDataViewColumn*>(column)]
autorelease]]
);
// setting the size related parameters:
+ int resizingMask;
if (column->IsResizeable())
{
- [nativeColumn setResizingMask:NSTableColumnUserResizingMask];
+ resizingMask = NSTableColumnUserResizingMask;
[nativeColumn setMinWidth:column->GetMinWidth()];
[nativeColumn setMaxWidth:column->GetMaxWidth()];
}
- else
+ else // column is not resizeable [by user]
{
- [nativeColumn setResizingMask:NSTableColumnNoResizing];
- [nativeColumn setMinWidth:column->GetWidth()];
- [nativeColumn setMaxWidth:column->GetWidth()];
+ // if the control doesn't show a header, make the columns resize
+ // automatically, this is particularly important for the single column
+ // controls (such as wxDataViewTreeCtrl) as their unique column should
+ // always take up all the available splace
+ resizingMask = column->GetOwner()->HasFlag(wxDV_NO_HEADER)
+ ? NSTableColumnAutoresizingMask
+ : NSTableColumnNoResizing;
}
- [nativeColumn setWidth:column->GetWidth()];
+ [nativeColumn setResizingMask:resizingMask];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
// setting the visibility:
return new wxCocoaDataViewControl(wxpeer,pos,size,style);
}
-// ============================================================================
-// wxPointerObject
-// ============================================================================
-
-@implementation wxPointerObject
-
--(id) init
-{
- self = [super init];
- if (self != nil)
- self->pointer = NULL;
- return self;
-}
-
--(id) initWithPointer:(void*) initPointer
-{
- self = [super init];
- if (self != nil)
- self->pointer = initPointer;
- return self;
-}
-
-//
-// inherited methods from NSObject
-//
--(BOOL) isEqual:(id)object
-{
- return (object != nil) &&
- ([object isKindOfClass:[wxPointerObject class]]) &&
- (pointer == [((wxPointerObject*) object) pointer]);
-}
-
--(NSUInteger) hash
-{
- return (NSUInteger) pointer;
-}
-
--(void*) pointer
-{
- return pointer;
-}
-
--(void) setPointer:(void*) newPointer
-{
- pointer = newPointer;
-}
-
-@end
-
// ============================================================================
// wxSortDescriptorObject
// ============================================================================
wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_DROP, dvc->GetId());
event.SetEventObject(dvc);
- event.SetItem(wxDataViewItem([((wxPointerObject*) item) pointer]));
+ event.SetItem(wxDataViewItemFromItem(item));
event.SetModel(dvc->GetModel());
BOOL dragSuccessful;
if ( [bestType compare:DataViewPboardType] == NSOrderedSame )
{
- NSArray* dataArray((NSArray*)[pasteboard propertyListForType:DataViewPboardType]);
+ NSArray* dataArray((NSArray*)
+ [pasteboard propertyListForType:DataViewPboardType]);
NSUInteger indexDraggedItem, noOfDraggedItems([dataArray count]);
indexDraggedItem = 0;
while (indexDraggedItem < noOfDraggedItems)
{
- wxDataObjectComposite* dataObjects(implementation->GetDnDDataObjects((NSData*)[dataArray objectAtIndex:indexDraggedItem]));
+ wxDataObjectComposite* dataObjects(
+ implementation->GetDnDDataObjects((NSData*)
+ [dataArray objectAtIndex:indexDraggedItem]));
if (dataObjects && (dataObjects->GetFormatCount() > 0))
{
// copy data into data object:
event.SetDataObject(dataObjects);
- event.SetDataFormat(implementation->GetDnDDataFormat(dataObjects));
+ event.SetDataFormat(
+ implementation->GetDnDDataFormat(dataObjects));
// copy data into buffer:
- dataObjects->GetDataHere(event.GetDataFormat().GetType(),buffer.GetWriteBuf(event.GetDataSize()));
+ dataObjects->GetDataHere(
+ event.GetDataFormat().GetType(),
+ buffer.GetWriteBuf(event.GetDataSize()));
buffer.UngetWriteBuf(event.GetDataSize());
event.SetDataBuffer(buffer.GetData());
// finally, send event:
}
else
{
- CFDataRef osxData; // needed to convert internally used UTF-16 representation to a UTF-8 representation
+ // needed to convert internally used UTF-16 representation to a UTF-8
+ // representation
+ CFDataRef osxData;
wxDataObjectComposite* dataObjects (new wxDataObjectComposite());
wxTextDataObject* textDataObject(new wxTextDataObject());
- osxData = ::CFStringCreateExternalRepresentation(kCFAllocatorDefault,(CFStringRef)[pasteboard stringForType:NSStringPboardType],kCFStringEncodingUTF8,32);
- if (textDataObject->SetData(::CFDataGetLength(osxData),::CFDataGetBytePtr(osxData)))
+ osxData = ::CFStringCreateExternalRepresentation
+ (
+ kCFAllocatorDefault,
+ (CFStringRef)[pasteboard stringForType:NSStringPboardType],
+ kCFStringEncodingUTF8,
+ 32
+ );
+ if (textDataObject->SetData(::CFDataGetLength(osxData),
+ ::CFDataGetBytePtr(osxData)))
dataObjects->Add(textDataObject);
else
delete textDataObject;
::CFRelease(osxData);
delete dataObjects;
}
+ return dragSuccessful;
}
--(id) outlineView:(NSOutlineView*)outlineView child:(NSInteger)index ofItem:(id)item
+-(id) outlineView:(NSOutlineView*)outlineView
+ child:(NSInteger)index
+ ofItem:(id)item
{
- if ((item == currentParentItem) && (index < ((NSInteger) [self getChildCount])))
+ if ((item == currentParentItem) &&
+ (index < ((NSInteger) [self getChildCount])))
return [self getChild:index];
- else
- {
- wxDataViewItemArray dataViewChildren;
- wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
- (void) model->GetChildren((item == nil) ? wxDataViewItem() : wxDataViewItem([((wxPointerObject*) item) pointer]),dataViewChildren);
- [self bufferItem:item withChildren:&dataViewChildren];
- if ([sortDescriptors count] > 0)
- [children sortUsingFunction:CompareItems context:sortDescriptors];
- return [self getChild:index];
- }
+ wxDataViewItemArray dataViewChildren;
+
+ wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
+ model->GetChildren(wxDataViewItemFromMaybeNilItem(item), dataViewChildren);
+ [self bufferItem:item withChildren:&dataViewChildren];
+ if ([sortDescriptors count] > 0)
+ [children sortUsingFunction:CompareItems context:sortDescriptors];
+ return [self getChild:index];
}
-(BOOL) outlineView:(NSOutlineView*)outlineView isItemExpandable:(id)item
{
wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
- return model->IsContainer(wxDataViewItem([((wxPointerObject*) item) pointer]));
+ return model->IsContainer(wxDataViewItemFromItem(item));
}
-(NSInteger) outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(id)item
wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
- noOfChildren = model->GetChildren((item == nil) ? wxDataViewItem() : wxDataViewItem([((wxPointerObject*) item) pointer]),dataViewChildren);
+ noOfChildren = model->GetChildren(wxDataViewItemFromMaybeNilItem(item),
+ dataViewChildren);
[self bufferItem:item withChildren:&dataViewChildren];
if ([sortDescriptors count] > 0)
[children sortUsingFunction:CompareItems context:sortDescriptors];
objectValueForTableColumn:(NSTableColumn*)tableColumn
byItem:(id)item
{
- wxDataViewColumn* col(static_cast<wxDataViewColumn*>([[tableColumn identifier] pointer]));
+ wxCHECK_MSG( model, nil, "Valid model in data source does not exist." );
- wxDataViewItem dataViewItem([((wxPointerObject*) item) pointer]);
+ wxDataViewColumn* col(static_cast<wxDataViewColumn*>([[tableColumn identifier] pointer]));
+ const unsigned colIdx = col->GetModelColumn();
- wxVariant value;
+ wxDataViewItem dataViewItem(wxDataViewItemFromItem(item));
+ if ( model->HasValue(dataViewItem, colIdx) )
+ {
+ wxVariant value;
+ model->GetValue(value,dataViewItem, colIdx);
+ col->GetRenderer()->SetValue(value);
+ }
- wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
- model->GetValue(value,dataViewItem,col->GetModelColumn());
- col->GetRenderer()->SetValue(value);
return nil;
}
{
wxDataViewColumn* col(static_cast<wxDataViewColumn*>([[tableColumn identifier] pointer]));
- wxDataViewItem dataViewItem([((wxPointerObject*) item) pointer]);
-
-
- if (((dynamic_cast<wxDataViewTextRenderer*>(col->GetRenderer()) != NULL) || (dynamic_cast<wxDataViewIconTextRenderer*>(col->GetRenderer()) != NULL)) &&
- ([object isKindOfClass:[NSString class]] == YES))
- {
- model->SetValue(wxVariant(wxCFStringRef([((NSString*) object) retain]).AsString()),dataViewItem,col->GetModelColumn()); // the string has to be retained before being passed to wxCFStringRef
- model->ValueChanged(dataViewItem,col->GetModelColumn());
- }
- else if (dynamic_cast<wxDataViewChoiceRenderer*>(col->GetRenderer()) != NULL)
- {
- if ([object isKindOfClass:[NSNumber class]] == YES)
- {
- model->SetValue(wxVariant(dynamic_cast<wxDataViewChoiceRenderer*>(col->GetRenderer())->GetChoice([((NSNumber*) object) intValue])),
- dataViewItem,col->GetModelColumn());
- model->ValueChanged(dataViewItem,col->GetModelColumn());
- }
- else if ([object isKindOfClass:[NSString class]] == YES) // do not know if this case can occur but initializing using strings works
- {
- model->SetValue(wxVariant(wxCFStringRef((NSString*) object).AsString()),dataViewItem,col->GetModelColumn());
- model->ValueChanged(dataViewItem,col->GetModelColumn());
- }
- }
- else if ((dynamic_cast<wxDataViewDateRenderer*>(col->GetRenderer()) != NULL) && ([object isKindOfClass:[NSDate class]] == YES))
- {
- wxDateTime wxDateTimeValue(1,wxDateTime::Jan,1970);
-
- wxLongLong seconds;
-
- seconds.Assign([((NSDate*) object) timeIntervalSince1970]); // get the number of seconds since 1970-01-01 UTC and this is
- // the only way to convert a double to a wxLongLong
- // the user has entered a date in the local timezone but seconds contains the number of seconds from date in the local timezone since 1970-01-01 UTC;
- // therefore, the timezone information has to be transferred to wxWidgets, too:
- wxDateTimeValue.Add(wxTimeSpan(0,0,seconds));
- wxDateTimeValue.MakeFromTimezone(wxDateTime::UTC);
- model->SetValue(wxVariant(wxDateTimeValue),dataViewItem,col->GetModelColumn());
- model->ValueChanged(dataViewItem,col->GetModelColumn());
- }
- else if ((dynamic_cast<wxDataViewToggleRenderer*>(col->GetRenderer()) != NULL) && ([object isKindOfClass:[NSNumber class]] == YES))
- {
- model->SetValue(wxVariant((bool) [((NSNumber*) object) boolValue]),dataViewItem,col->GetModelColumn());
- model->ValueChanged(dataViewItem,col->GetModelColumn());
- }
+ col->GetRenderer()->
+ OSXOnCellChanged(object, wxDataViewItemFromItem(item), col->GetModelColumn());
}
-(void) outlineView:(NSOutlineView*)outlineView sortDescriptorsDidChange:(NSArray*)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.
{
+ // 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.
NSArray* newDescriptors;
NSMutableArray* wxSortDescriptors;
wxSortDescriptors = [NSMutableArray arrayWithCapacity:noOfDescriptors];
for (NSUInteger i=0; i<noOfDescriptors; ++i)
{
- // constant definition for abbreviational purposes:
NSSortDescriptor* const newDescriptor = [newDescriptors objectAtIndex:i];
[wxSortDescriptors addObject:[[[wxSortDescriptorObject alloc] initWithModelPtr:model
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:
+ // 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
event.SetEventObject(dvc);
if (noOfDescriptors > 0)
{
- // constant definition for abbreviational purposes:
wxDataViewColumn* const col = [[wxSortDescriptors objectAtIndex:0] columnPtr];
event.SetColumn(dvc->GetColumnPosition(col));
event(wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE,dvc->GetId());
event.SetEventObject(dvc);
- event.SetItem(wxDataViewItem([((wxPointerObject*) item) pointer]));
+ event.SetItem(wxDataViewItemFromItem(item));
event.SetModel(dvc->GetModel());
if ([bestType compare:DataViewPboardType] == NSOrderedSame)
{
}
else
{
- CFDataRef osxData; // needed to convert internally used UTF-16 representation to a UTF-8 representation
+ // needed to convert internally used UTF-16 representation to a UTF-8
+ // representation
+ CFDataRef osxData;
wxDataObjectComposite* dataObjects (new wxDataObjectComposite());
wxTextDataObject* textDataObject(new wxTextDataObject());
}
-(BOOL) outlineView:(NSOutlineView*)outlineView writeItems:(NSArray*)writeItems toPasteboard:(NSPasteboard*)pasteboard
-// 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 if for all items a
-// string representation exists
{
+ // 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
+ // if for all items a string representation exists
wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
wxDataViewItemArray dataViewItems;
NSMutableArray* dataArray = [[NSMutableArray arrayWithCapacity:[writeItems count]] retain]; // 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:
+ // 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());
wxDataObjectComposite* itemObject(new wxDataObjectComposite()); // data object for current item
wxString itemString; // contains the TAB concatenated data of an item
- event.SetItem(wxDataViewItem([((wxPointerObject*) [writeItems objectAtIndex:itemCounter]) pointer]));
+ event.SetItem(
+ wxDataViewItemFromItem([writeItems objectAtIndex:itemCounter]));
itemString = ::ConcatenateDataViewItemValues(dvc,event.GetItem());
itemObject->Add(new wxTextDataObject(itemString));
event.SetDataObject(itemObject);
// check if event has not been vetoed:
if (dvc->HandleWindowEvent(event) && event.IsAllowed() && (event.GetDataObject()->GetFormatCount() > 0))
{
- // constant definition for abbreviational purposes:
size_t const noOfFormats = event.GetDataObject()->GetFormatCount();
- // variable definition and initialization:
wxDataFormat* dataFormats(new wxDataFormat[noOfFormats]);
event.GetDataObject()->GetAllFormats(dataFormats,wxDataObject::Get);
switch (idDataFormat)
{
case wxDF_TEXT:
- if (!itemStringAvailable) // otherwise wxDF_UNICODETEXT already filled up the string; and the UNICODE representation has priority
+ // otherwise wxDF_UNICODETEXT already filled up
+ // the string; and the UNICODE representation has
+ // priority
+ if (!itemStringAvailable)
{
event.GetDataObject()->GetDataHere(wxDF_TEXT,dataBuffer.GetAppendBuf(dataSize));
dataBuffer.UngetAppendBuf(dataSize);
{
wxCustomRendererObject * const
obj = static_cast<wxCustomRendererObject *>([self objectValue]);
- wxDataViewCustomRenderer * const renderer = obj->customRenderer;
+ if ( !obj )
+ {
+ // this may happen for the custom cells in container rows: they don't
+ // have any values
+ return;
+ }
- // draw its own background:
- [[self backgroundColor] set];
- NSRectFill(cellFrame);
+ wxDataViewCustomRenderer * const renderer = obj->customRenderer;
- // TODO: attributes support
- renderer->Render(wxFromNSRect(controlView, cellFrame), renderer->GetDC(), 0);
+ wxDC * const dc = renderer->GetDC();
+ renderer->WXCallRender(wxFromNSRect(controlView, cellFrame), dc, 0);
renderer->SetDC(NULL);
}
{
CGFloat const cellSpace = cellFrame.size.width-[self cellSize].width;
- if (cellSpace <= 0) // if the cell's frame is smaller than its contents (at least in x-direction) make sure that the image is visible:
+ // if the cell's frame is smaller than its contents (at least
+ // in x-direction) make sure that the image is visible:
+ if (cellSpace <= 0)
NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText,NSMinXEdge);
else // otherwise center the image and text in the cell's frame
NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText+0.5*cellSpace,NSMinXEdge);
{
CGFloat const cellSpace = cellFrame.size.width-[self cellSize].width;
- if (cellSpace <= 0) // if the cell's frame is smaller than its contents (at least in x-direction) make sure that the image is visible:
+ // if the cell's frame is smaller than its contents (at least
+ // in x-direction) make sure that the image is visible:
+ if (cellSpace <= 0)
NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText,NSMinXEdge);
else // otherwise right align the image and text in the cell's frame
NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText+cellSpace,NSMinXEdge);
[self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame];
// draw the image part by ourselves;
- // check if the cell has to draw its own background (checking is done by the parameter of the textfield's cell):
+ // check if the cell has to draw its own background (checking is done by
+ // the parameter of the textfield's cell):
if ([self drawsBackground])
{
[[self backgroundColor] set];
}
if (image != nil)
{
- // the image is slightly shifted (xImageShift) and has a fixed size but the image's frame might be larger and starts
- // currently on the left side of the cell's frame; therefore, the origin and the image's frame size have to be adjusted:
+ // the image is slightly shifted (xImageShift) and has a fixed size
+ // but the image's frame might be larger and starts currently on the
+ // left side of the cell's frame; therefore, the origin and the
+ // image's frame size have to be adjusted:
if (imageFrame.size.width >= xImageShift+imageSize.width+spaceImageText)
{
imageFrame.origin.x += imageFrame.size.width-imageSize.width-spaceImageText;
imageFrame.size.height = imageSize.height;
imageFrame.origin.y += ceil(0.5*(cellFrame.size.height-imageFrame.size.height));
- // according to the documentation the coordinate system should be flipped for NSTableViews (y-coordinate goes from top to bottom);
- // to draw an image correctly the coordinate system has to be transformed to a bottom-top coordinate system, otherwise the image's
+ // according to the documentation the coordinate system should be
+ // flipped for NSTableViews (y-coordinate goes from top to bottom); to
+ // draw an image correctly the coordinate system has to be transformed
+ // to a bottom-top coordinate system, otherwise the image's
// content is flipped:
NSAffineTransform* coordinateTransform([NSAffineTransform transform]);
}
[image drawInRect:imageFrame fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; // suggested method to draw the image
// instead of compositeToPoint:operation:
- // take back previous transformation (if the view is not flipped the coordinate transformation matrix contains the identity matrix
- // and the next two operations do not change the content's transformation matrix):
+ // take back previous transformation (if the view is not flipped the
+ // coordinate transformation matrix contains the identity matrix and
+ // the next two operations do not change the content's transformation
+ // matrix):
[coordinateTransform invert];
[coordinateTransform concat];
}
// let the textfield cell draw the text part:
- if (textFrame.size.width > [self cellTextSize].width) // for unknown reasons the alignment of the text cell is ignored; therefore change the size so that
- textFrame.size.width = [self cellTextSize].width; // alignment does not influence the visualization anymore
+ if (textFrame.size.width > [self cellTextSize].width)
+ {
+ // for unknown reasons the alignment of the text cell is ignored;
+ // therefore change the size so that alignment does not influence the
+ // visualization anymore
+ textFrame.size.width = [self cellTextSize].width;
+ }
[super drawWithFrame:textFrame inView:controlView];
}
if (imageFrame.size.height > imageSize.height)
imageFrame.size.height = imageSize.height;
imageFrame.origin.y += ceil(0.5*(cellFrame.size.height-imageFrame.size.height));
- // If the point is in the image rect, then it is a content hit (see documentation for hitTestForEvent:inRect:ofView):
+ // If the point is in the image rect, then it is a content hit (see
+ // documentation for hitTestForEvent:inRect:ofView):
if (NSMouseInRect(point, imageFrame, [controlView isFlipped]))
return NSCellHitContentArea;
}
// if the image was not hit let's try the text part:
- if (textFrame.size.width > [self cellTextSize].width) // for unknown reasons the alignment of the text cell is ignored; therefore change the size so that
- textFrame.size.width = [self cellTextSize].width; // alignment does not influence the visualization anymore
+ if (textFrame.size.width > [self cellTextSize].width)
+ {
+ // for unknown reasons the alignment of the text cell is ignored;
+ // therefore change the size so that alignment does not influence the
+ // visualization anymore
+ textFrame.size.width = [self cellTextSize].width;
+ }
+
return [super hitTestForEvent:event inRect:textFrame ofView:controlView];
}
#endif
// actions
//
-(void) actionDoubleClick:(id)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
{
+ // 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()); // variable definition
+ wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED,dvc->GetId());
event.SetEventObject(dvc);
- event.SetItem(wxDataViewItem([((wxPointerObject*) [self itemAtRow:[self clickedRow]]) pointer]));
+ event.SetItem(wxDataViewItemFromItem([self itemAtRow:[self clickedRow]]));
dvc->GetEventHandler()->ProcessEvent(event);
}
// contextual menus
//
-(NSMenu*) menuForEvent:(NSEvent*)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
{
+ // 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());
event.SetEventObject(dvc);
event.SetModel(dvc->GetModel());
// get the item information;
- // theoretically more than one ID can be returned but the event can only handle one item, therefore only the first
- // item of the array is returned:
+ // theoretically more than one ID can be returned but the event can only
+ // handle one item, therefore only the first item of the array is
+ // returned:
if (dvc->GetSelections(selectedItems) > 0)
event.SetItem(selectedItems[0]);
dvc->GetEventHandler()->ProcessEvent(event);
dvc->HandleWindowEvent(event);
// now, check if the click may have had an influence on sorting, too;
- // the sorting setup has to be done only if the clicked table column is sortable and has not been used for
- // sorting before the click; if the column is already responsible for sorting the native control changes
- // the sorting direction automatically and informs the data source via outlineView:sortDescriptorsDidChange:
+ // the sorting setup has to be done only if the clicked table column is
+ // sortable and has not been used for sorting before the click; if the
+ // column is already responsible for sorting the native control changes
+ // the sorting direction automatically and informs the data source via
+ // outlineView:sortDescriptorsDidChange:
if (col->IsSortable() && ([tableColumn sortDescriptorPrototype] == nil))
{
- // remove the sort order from the previously sorted column table (it can also be that
- // no sorted column table exists):
+ // remove the sort order from the previously sorted column table (it
+ // can also be that no sorted column table exists):
UInt32 const noOfColumns = [outlineView numberOfColumns];
for (UInt32 i=0; i<noOfColumns; ++i)
{
wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
- wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,dvc->GetId()); // variable definition
+ wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,dvc->GetId());
event.SetEventObject(dvc);
- event.SetItem (wxDataViewItem([((wxPointerObject*) item) pointer]));
+ event.SetItem (wxDataViewItemFromItem(item));
event.SetModel (dvc->GetModel());
// finally send the equivalent wxWidget event:
dvc->GetEventHandler()->ProcessEvent(event);
{
wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
- wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING,dvc->GetId()); // variable definition
+ wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING,dvc->GetId());
event.SetEventObject(dvc);
- event.SetItem (wxDataViewItem([((wxPointerObject*) item) pointer]));
+ event.SetItem (wxDataViewItemFromItem(item));
event.SetModel (dvc->GetModel());
// finally send the equivalent wxWidget event:
dvc->GetEventHandler()->ProcessEvent(event);
}
-(void) outlineView:(wxCocoaOutlineView*)outlineView
-willDisplayCell:(id)cell
-forTableColumn:(NSTableColumn*)tableColumn
-item:(id)item
+ willDisplayCell:(id)cell
+ forTableColumn:(NSTableColumn*)tableColumn
+ item:(id)item
{
wxDataViewCtrl * const dvc = implementation->GetDataViewCtrl();
wxDataViewModel * const model = dvc->GetModel();
[[tableColumn identifier] pointer]
)
);
+ const unsigned colIdx = dvCol->GetModelColumn();
- wxDataViewRenderer * const renderer = dvCol->GetRenderer();
- wxDataViewRendererNativeData * const data = renderer->GetNativeData();
-
- wxDataViewItem dvItem([static_cast<wxPointerObject *>(item) pointer]);
-
- // set the font and text colour to use: we need to do it if we had ever
- // changed them before, even if this item itself doesn't have any special
- // attributes as otherwise it would reuse the attributes from the previous
- // cell rendered using the same renderer
- NSFont *font = NULL;
- NSColor *colText = NULL;
-
- wxDataViewItemAttr attr;
- if ( model && model->GetAttr(dvItem, dvCol->GetModelColumn(), attr) )
- {
- if ( attr.HasFont() )
- {
- font = data->GetOriginalFont();
- if ( !font )
- {
- // this is the first time we're setting the font, remember the
- // original one before changing it
- font = [cell font];
- data->SaveOriginalFont(font);
- }
-
- if ( font )
- {
- // FIXME: using wxFont methods here doesn't work for some reason
- NSFontManager * const fm = [NSFontManager sharedFontManager];
- if ( attr.GetBold() )
- font = [fm convertFont:font toHaveTrait:NSBoldFontMask];
- if ( attr.GetItalic() )
- font = [fm convertFont:font toHaveTrait:NSItalicFontMask];
- }
- //else: can't change font if the cell doesn't have any
- }
+ wxDataViewItem dvItem(wxDataViewItemFromItem(item));
- if ( attr.HasColour() )
- {
- // we can set font for any cell but only NSTextFieldCell provides
- // a method for setting text colour so check that this method is
- // available before using it
- if ( [cell respondsToSelector:@selector(setTextColor:)] &&
- [cell respondsToSelector:@selector(textColor)] )
- {
- if ( !data->GetOriginalTextColour() )
- {
- data->SaveOriginalTextColour([cell textColor]);
- }
-
- const wxColour& c = attr.GetColour();
- colText = [NSColor colorWithDeviceRed:c.Red() / 255.
- green:c.Green() / 255.
- blue:c.Blue() / 255.
- alpha:c.Alpha() / 255.];
- }
- }
- }
-
- if ( !font )
- font = data->GetOriginalFont();
- if ( !colText )
- colText = data->GetOriginalTextColour();
-
- if ( font )
- [cell setFont:font];
-
- if ( colText )
- [cell setTextColor:colText];
+ if ( !model->HasValue(dvItem, colIdx) )
+ return;
+ wxDataViewRenderer * const renderer = dvCol->GetRenderer();
+ wxDataViewRendererNativeData * const data = renderer->GetNativeData();
+ // let the renderer know about what it's going to render next
data->SetColumnPtr(tableColumn);
data->SetItem(item);
data->SetItemCell(cell);
+ // use the attributes: notice that we need to do this whether we have them
+ // or not as even if this cell doesn't have any attributes, the previous
+ // one might have had some and then we need to reset them back to default
+ wxDataViewItemAttr attr;
+ 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();
}
event.SetEventObject(dvc);
- event.SetItem(wxDataViewItem([((wxPointerObject*) [[notification userInfo] objectForKey:@"NSObject"]) pointer]));
+ event.SetItem(wxDataViewItemFromItem(
+ [[notification userInfo] objectForKey:@"NSObject"]));
dvc->GetEventHandler()->ProcessEvent(event);
}
event.SetEventObject(dvc);
- event.SetItem(wxDataViewItem([((wxPointerObject*) [[notification userInfo] objectForKey:@"NSObject"]) pointer]));
+ event.SetItem(wxDataViewItemFromItem(
+ [[notification userInfo] objectForKey:@"NSObject"]));
dvc->GetEventHandler()->ProcessEvent(event);
}
{
wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
- wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED,dvc->GetId()); // variable definition
-
+ wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED,dvc->GetId());
event.SetEventObject(dvc);
- event.SetModel (dvc->GetModel());
+ event.SetModel(dvc->GetModel());
+ event.SetItem(dvc->GetSelection());
dvc->GetEventHandler()->ProcessEvent(event);
}
-(void) textDidBeginEditing:(NSNotification*)notification
-// this notification is only sent if the user started modifying the cell (not when the user clicked into the cell
-// and the cell's editor is called!)
{
- // call method of superclass (otherwise editing does not work correctly - the outline data source class is not
- // informed about a change of data):
+ // this notification is only sent if the user started modifying the cell
+ // (not when the user clicked into the cell and the cell's editor is
+ // called!)
+
+ // call method of superclass (otherwise editing does not work correctly -
+ // the outline data source class is not informed about a change of data):
[super textDidBeginEditing:notification];
// remember the column being edited, it will be used in textDidEndEditing:
// now, send the event:
wxDataViewEvent
- event(wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_STARTED,dvc->GetId()); // variable definition
+ event(wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_STARTED,dvc->GetId());
event.SetEventObject(dvc);
event.SetItem(
- wxDataViewItem([((wxPointerObject*) [self itemAtRow:currentlyEditedRow]) pointer]));
+ wxDataViewItemFromItem([self itemAtRow:currentlyEditedRow]));
event.SetColumn(dvc->GetColumnPosition(col));
event.SetDataViewColumn(col);
dvc->GetEventHandler()->ProcessEvent(event);
-(void) textDidEndEditing:(NSNotification*)notification
{
- // call method of superclass (otherwise editing does not work correctly - the outline data source class is not
- // informed about a change of data):
+ // call method of superclass (otherwise editing does not work correctly -
+ // the outline data source class is not informed about a change of data):
[super textDidEndEditing:notification];
- // under OSX an event indicating the end of an editing session can be sent 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 been sent the last edited column/row are valid:
+ // under OSX an event indicating the end of an editing session can be sent
+ // 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
+ // been sent the last edited column/row are valid:
if ( currentlyEditedColumn != -1 && currentlyEditedRow != -1 )
{
wxDataViewColumn* const col =
// send event to wxWidgets:
wxDataViewEvent
- event(wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_DONE,dvc->GetId()); // variable definition
+ event(wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_DONE,dvc->GetId());
event.SetEventObject(dvc);
event.SetItem(
- wxDataViewItem([((wxPointerObject*) [self itemAtRow:currentlyEditedRow]) pointer]));
+ wxDataViewItemFromItem([self itemAtRow:currentlyEditedRow]));
event.SetColumn(dvc->GetColumnPosition(col));
event.SetDataViewColumn(col);
dvc->GetEventHandler()->ProcessEvent(event);
}
@end
+
// ============================================================================
// wxCocoaDataViewControl
// ============================================================================
-//
-// constructors / destructor
-//
- wxCocoaDataViewControl::wxCocoaDataViewControl(wxWindow* peer, const wxPoint& pos, const wxSize& size, long style)
-:wxWidgetCocoaImpl(peer,[[NSScrollView alloc] initWithFrame:wxOSXGetFrameForControl(peer,pos,size)]),
- m_DataSource(NULL), m_OutlineView([[wxCocoaOutlineView alloc] init])
+
+wxCocoaDataViewControl::wxCocoaDataViewControl(wxWindow* peer,
+ const wxPoint& pos,
+ const wxSize& size,
+ long style)
+ : wxWidgetCocoaImpl
+ (
+ peer,
+ [[NSScrollView alloc] initWithFrame:wxOSXGetFrameForControl(peer,pos,size)]
+ ),
+ m_DataSource(NULL),
+ m_OutlineView([[wxCocoaOutlineView alloc] init])
{
// initialize scrollview (the outline view is part of a scrollview):
- NSScrollView* scrollview = (NSScrollView*) GetWXWidget(); // definition for abbreviational purposes
-
+ NSScrollView* scrollview = (NSScrollView*) GetWXWidget();
[scrollview setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[scrollview setBorderType:NSNoBorder];
[scrollview setAutohidesScrollers:YES];
[scrollview setDocumentView:m_OutlineView];
- // setting up the native control itself
- NSUInteger maskGridStyle(NSTableViewGridNone);
+ // initialize the native control itself too
+ InitOutlineView(style);
+}
+void wxCocoaDataViewControl::InitOutlineView(long style)
+{
[m_OutlineView setImplementation:this];
[m_OutlineView setColumnAutoresizingStyle:NSTableViewSequentialColumnAutoresizingStyle];
[m_OutlineView setIndentationPerLevel:GetDataViewCtrl()->GetIndent()];
+ NSUInteger maskGridStyle(NSTableViewGridNone);
if (style & wxDV_HORIZ_RULES)
maskGridStyle |= NSTableViewSolidHorizontalGridLineMask;
if (style & wxDV_VERT_RULES)
[m_OutlineView setGridStyleMask:maskGridStyle];
[m_OutlineView setAllowsMultipleSelection: (style & wxDV_MULTIPLE) != 0];
[m_OutlineView setUsesAlternatingRowBackgroundColors:(style & wxDV_ROW_LINES) != 0];
+
+ if ( style & wxDV_NO_HEADER )
+ [m_OutlineView setHeaderView:nil];
}
wxCocoaDataViewControl::~wxCocoaDataViewControl()
//
bool wxCocoaDataViewControl::ClearColumns()
{
- bool const bufAllowsMultipleSelection = [m_OutlineView allowsMultipleSelection];
-
-
- // as there is a bug in NSOutlineView version (OSX 10.5.6 #6555162) the columns cannot be deleted if there is an outline column in the view;
+ // as there is a bug in NSOutlineView version (OSX 10.5.6 #6555162) the
+ // columns cannot be deleted if there is an outline column in the view;
// therefore, the whole view is deleted and newly constructed:
[m_OutlineView release];
m_OutlineView = [[wxCocoaOutlineView alloc] init];
[((NSScrollView*) GetWXWidget()) setDocumentView:m_OutlineView];
-
- // setting up the native control itself
- [m_OutlineView setImplementation:this];
- [m_OutlineView setColumnAutoresizingStyle:NSTableViewSequentialColumnAutoresizingStyle];
- [m_OutlineView setIndentationPerLevel:GetDataViewCtrl()->GetIndent()];
- if (bufAllowsMultipleSelection)
- [m_OutlineView setAllowsMultipleSelection:YES];
[m_OutlineView setDataSource:m_DataSource];
- // done:
+
+ InitOutlineView(GetDataViewCtrl()->GetWindowStyle());
+
return true;
}
// create column and set the native data of the dataview column:
NSTableColumn *nativeColumn = ::CreateNativeColumn(columnPtr);
columnPtr->GetNativeData()->SetNativeColumnPtr(nativeColumn);
- // as the native control does not allow the insertion of a column at a specified position the column is first appended and
- // - if necessary - moved to its final position:
+ // as the native control does not allow the insertion of a column at a
+ // specified position the column is first appended and - if necessary -
+ // moved to its final position:
[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)
//
//
// selection related methods (inherited from wxDataViewWidgetImpl)
//
+
+wxDataViewItem wxCocoaDataViewControl::GetCurrentItem() const
+{
+ return wxDataViewItem([[m_OutlineView itemAtRow:[m_OutlineView selectedRow]] pointer]);
+}
+
+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::GetSelections(wxDataViewItemArray& sel) const
{
NSIndexSet* selectedRowIndexes([m_OutlineView selectedRowIndexes]);
{
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()
}
}
+id wxCocoaDataViewControl::GetItemAtRow(int row) const
+{
+ return [m_OutlineView itemAtRow:row];
+}
+
// ----------------------------------------------------------------------------
// wxDataViewRendererNativeData
// ----------------------------------------------------------------------------
// ---------------------------------------------------------
// wxDataViewRenderer
// ---------------------------------------------------------
- wxDataViewRenderer::wxDataViewRenderer(const wxString& varianttype, wxDataViewCellMode mode, int align)
-:wxDataViewRendererBase(varianttype,mode,align), m_alignment(align), m_mode(mode), m_NativeDataPtr(NULL)
+
+wxDataViewRenderer::wxDataViewRenderer(const wxString& varianttype,
+ wxDataViewCellMode mode,
+ int align)
+ : wxDataViewRendererBase(varianttype, mode, align),
+ m_alignment(align),
+ m_mode(mode),
+ m_NativeDataPtr(NULL)
{
}
return GetNativeData()->GetEllipsizeMode();
}
+void
+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
+
+ wxVariant value;
+ if ( [object isKindOfClass:[NSString class]] )
+ value = ObjectToString(object);
+ else if ( [object isKindOfClass:[NSNumber class]] )
+ value = ObjectToLong(object);
+ else if ( [object isKindOfClass:[NSDate class]] )
+ value = ObjectToDate(object);
+ else
+ {
+ wxFAIL_MSG( wxString::Format
+ (
+ "unknown value type %s",
+ wxCFStringRef::AsString([object className])
+ ));
+ return;
+ }
+
+ wxDataViewModel *model = GetOwner()->GetOwner()->GetModel();
+ model->ChangeValue(value, item, col);
+}
+
+void wxDataViewRenderer::OSXApplyAttr(const wxDataViewItemAttr& attr)
+{
+ wxDataViewRendererNativeData * const data = GetNativeData();
+ NSCell * const cell = data->GetItemCell();
+
+ // set the font and text colour to use: we need to do it if we had ever
+ // changed them before, even if this item itself doesn't have any special
+ // attributes as otherwise it would reuse the attributes from the previous
+ // cell rendered using the same renderer
+ NSFont *font = NULL;
+ NSColor *colText = NULL;
+
+ if ( attr.HasFont() )
+ {
+ font = data->GetOriginalFont();
+ if ( !font )
+ {
+ // this is the first time we're setting the font, remember the
+ // original one before changing it
+ font = [cell font];
+ data->SaveOriginalFont(font);
+ }
+
+ if ( font )
+ {
+ // FIXME: using wxFont methods here doesn't work for some reason
+ NSFontManager * const fm = [NSFontManager sharedFontManager];
+ if ( attr.GetBold() )
+ font = [fm convertFont:font toHaveTrait:NSBoldFontMask];
+ if ( attr.GetItalic() )
+ font = [fm convertFont:font toHaveTrait:NSItalicFontMask];
+ }
+ //else: can't change font if the cell doesn't have any
+ }
+
+ if ( attr.HasColour() )
+ {
+ // we can set font for any cell but only NSTextFieldCell provides
+ // a method for setting text colour so check that this method is
+ // available before using it
+ if ( [cell respondsToSelector:@selector(setTextColor:)] &&
+ [cell respondsToSelector:@selector(textColor)] )
+ {
+ if ( !data->GetOriginalTextColour() )
+ {
+ // the cast to (untyped) id is safe because of the check above
+ data->SaveOriginalTextColour([(id)cell textColor]);
+ }
+
+ const wxColour& c = attr.GetColour();
+ colText = [NSColor colorWithDeviceRed:c.Red() / 255.
+ green:c.Green() / 255.
+ blue:c.Blue() / 255.
+ alpha:c.Alpha() / 255.];
+ }
+ }
+
+ if ( !font )
+ font = data->GetOriginalFont();
+ if ( !colText )
+ colText = data->GetOriginalTextColour();
+
+ if ( font )
+ [cell setFont:font];
+
+ if ( colText )
+ [(id)cell setTextColor:colText];
+}
+
+void wxDataViewRenderer::OSXApplyEnabled(bool enabled)
+{
+ [GetNativeData()->GetItemCell() setEnabled:enabled];
+}
+
IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer,wxDataViewRendererBase)
// ---------------------------------------------------------
wxDataViewCustomRenderer::wxDataViewCustomRenderer(const wxString& varianttype,
wxDataViewCellMode mode,
int align)
- : wxDataViewRenderer(varianttype, mode, align),
+ : wxDataViewCustomRendererBase(varianttype, mode, align),
m_editorCtrlPtr(NULL),
m_DCPtr(NULL)
{
return true;
}
+void wxDataViewCustomRenderer::OSXApplyAttr(const wxDataViewItemAttr& attr)
+{
+ // simply save the attribute so that it could be reused from our Render()
+ SetAttr(attr);
+
+ // it's not necessary to call the base class version which sets the cell
+ // properties to correspond to this attribute because we currently don't
+ // use any NSCell methods in custom renderers anyhow but if we ever start
+ // doing this (e.g. override RenderText() here to use NSTextFieldCell
+ // methods), then we should pass it on to wxDataViewRenderer here
+}
+
IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer)
// ---------------------------------------------------------
}
}
+void
+wxDataViewTextRenderer::OSXOnCellChanged(NSObject *value,
+ const wxDataViewItem& item,
+ unsigned col)
+{
+ wxDataViewModel *model = GetOwner()->GetOwner()->GetModel();
+ model->ChangeValue(ObjectToString(value), item, col);
+}
+
IMPLEMENT_CLASS(wxDataViewTextRenderer,wxDataViewRenderer)
// ---------------------------------------------------------
[cell release];
}
+// This method returns 'true' if
+// - the passed bitmap is valid and it could be assigned to the native data
+// browser;
+// - the passed bitmap is invalid (or is not initialized); this case
+// simulates a non-existing bitmap.
+// In all other cases the method returns 'false'.
bool wxDataViewBitmapRenderer::MacRender()
- // This method returns 'true' if
- // - the passed bitmap is valid and it could be assigned to the native data browser;
- // - the passed bitmap is invalid (or is not initialized); this case simulates a non-existing bitmap.
- // In all other cases the method returns 'false'.
{
wxCHECK_MSG(GetValue().GetType() == GetVariantType(),false,wxString("Bitmap renderer cannot render value; value type: ") << GetValue().GetType());
wxDataViewChoiceRenderer::wxDataViewChoiceRenderer(const wxArrayString& choices,
wxDataViewCellMode mode,
int alignment)
- : wxDataViewRenderer(wxT("string"),mode,alignment), m_Choices(choices)
+ : wxDataViewRenderer(wxT("string"), mode, alignment),
+ m_choices(choices)
{
NSPopUpButtonCell* cell;
if (GetValue().GetDateTime().IsValid())
{
// -- find best fitting style to show the date --
- // as the style should be identical for all cells a reference date instead of the actual cell's date
- // value is used for all cells; this reference date is stored in the renderer's native data section
- // for speed purposes; otherwise, the reference date's string has to be recalculated for each item that
- // may become timewise long if a lot of rows using dates exist;
- // the algorithm has the preference to display as much information as possible in the first instance;
- // but as this is often impossible due to space restrictions the style is shortened per loop; finally,
- // if the shortest time and date format does not fit into the cell the time part is dropped;
- // remark: the time part itself is not modified per iteration loop and only uses the short style,
- // means that only the hours and minutes are being shown
- [GetNativeData()->GetItemCell() setObjectValue:GetNativeData()->GetObject()]; // GetObject() returns a date for testing the size of a date object
+ // as the style should be identical for all cells a reference date
+ // instead of the actual cell's date value is used for all cells;
+ // this reference date is stored in the renderer's native data
+ // section for speed purposes; otherwise, the reference date's
+ // string has to be recalculated for each item that may become
+ // timewise long if a lot of rows using dates exist; the algorithm
+ // has the preference to display as much information as possible
+ // in the first instance; but as this is often impossible due to
+ // space restrictions the style is shortened per loop; finally, if
+ // the shortest time and date format does not fit into the cell
+ // the time part is dropped; remark: the time part itself is not
+ // modified per iteration loop and only uses the short style,
+ // means that only the hours and minutes are being shown
+
+ // GetObject() returns a date for testing the size of a date object
+ [GetNativeData()->GetItemCell() setObjectValue:GetNativeData()->GetObject()];
[[GetNativeData()->GetItemCell() formatter] setTimeStyle:NSDateFormatterShortStyle];
for (int dateFormatterStyle=4; dateFormatterStyle>0; --dateFormatterStyle)
{
[[GetNativeData()->GetItemCell() formatter] setDateStyle:(NSDateFormatterStyle)dateFormatterStyle];
if (dateFormatterStyle == 1)
{
- // if the shortest style for displaying the date and time is too long to be fully visible remove the time part of the date:
+ // if the shortest style for displaying the date and time
+ // is too long to be fully visible remove the time part of
+ // the date:
if ([GetNativeData()->GetItemCell() cellSize].width > [GetNativeData()->GetColumnPtr() width])
[[GetNativeData()->GetItemCell() formatter] setTimeStyle:NSDateFormatterNoStyle];
- break; // basically not necessary as the loop would end anyway but let's save the last comparison
+ {
+ // basically not necessary as the loop would end anyway
+ // but let's save the last comparison
+ break;
+ }
}
else if ([GetNativeData()->GetItemCell() cellSize].width <= [GetNativeData()->GetColumnPtr() width])
break;
}
- // set data (the style is set by the previous loop);
- // on OSX the date has to be specified with respect to UTC; in wxWidgets the date is always entered in the local timezone; so, we have to do a conversion
- // from the local to UTC timezone when adding the seconds to 1970-01-01 UTC:
+ // set data (the style is set by the previous loop); on OSX the
+ // date has to be specified with respect to UTC; in wxWidgets the
+ // date is always entered in the local timezone; so, we have to do
+ // a conversion from the local to UTC timezone when adding the
+ // seconds to 1970-01-01 UTC:
[GetNativeData()->GetItemCell() setObjectValue:[NSDate dateWithTimeIntervalSince1970:GetValue().GetDateTime().ToUTC().Subtract(wxDateTime(1,wxDateTime::Jan,1970)).GetSeconds().ToDouble()]];
}
return true;
}
}
+void
+wxDataViewDateRenderer::OSXOnCellChanged(NSObject *value,
+ const wxDataViewItem& item,
+ unsigned col)
+{
+ wxDataViewModel *model = GetOwner()->GetOwner()->GetModel();
+ model->ChangeValue(ObjectToDate(value), item, col);
+}
+
IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer,wxDataViewRenderer)
// ---------------------------------------------------------
}
}
+void
+wxDataViewIconTextRenderer::OSXOnCellChanged(NSObject *value,
+ const wxDataViewItem& item,
+ unsigned col)
+{
+ wxVariant valueIconText;
+ valueIconText << wxDataViewIconText(ObjectToString(value));
+
+ wxDataViewModel *model = GetOwner()->GetOwner()->GetModel();
+ model->ChangeValue(valueIconText, item, col);
+}
+
IMPLEMENT_ABSTRACT_CLASS(wxDataViewIconTextRenderer,wxDataViewRenderer)
// ---------------------------------------------------------
}
}
+void
+wxDataViewToggleRenderer::OSXOnCellChanged(NSObject *value,
+ const wxDataViewItem& item,
+ unsigned col)
+{
+ wxDataViewModel *model = GetOwner()->GetOwner()->GetModel();
+ model->ChangeValue(ObjectToBool(value), item, col);
+}
+
IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer,wxDataViewRenderer)
// ---------------------------------------------------------
}
}
+void
+wxDataViewProgressRenderer::OSXOnCellChanged(NSObject *value,
+ const wxDataViewItem& item,
+ unsigned col)
+{
+ wxDataViewModel *model = GetOwner()->GetOwner()->GetModel();
+ model->ChangeValue(ObjectToLong(value), item, col);
+}
+
IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer,wxDataViewRenderer)
// ---------------------------------------------------------
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);
}
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);
}
delete m_NativeDataPtr;
}
+int wxDataViewColumn::GetWidth() const
+{
+ return [m_NativeDataPtr->GetNativeColumnPtr() width];
+}
+
bool wxDataViewColumn::IsSortKey() const
{
NSTableColumn *nsCol = GetNativeData()->GetNativeColumnPtr();
{
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);
}
void wxDataViewColumn::SetBitmap(const wxBitmap& bitmap)
{
- // bitmaps and titles cannot exist at the same time - if the bitmap is set the title is removed:
+ // bitmaps and titles cannot exist at the same time - if the bitmap is set
+ // the title is removed:
m_title = wxEmptyString;
wxDataViewColumnBase::SetBitmap(bitmap);
[[m_NativeDataPtr->GetNativeColumnPtr() headerCell] setImage:[[bitmap.GetNSImage() retain] autorelease]];
{
}
+void wxDataViewColumn::SetHidden(bool hidden)
+{
+ // How to set flag here?
+
+ [m_NativeDataPtr->GetNativeColumnPtr() setHidden:hidden];
+}
+
+bool wxDataViewColumn::IsHidden() const
+{
+ return [m_NativeDataPtr->GetNativeColumnPtr() isHidden];
+}
+
void wxDataViewColumn::SetResizeable(bool resizeable)
{
wxDataViewColumnBase::SetResizeable(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)
void wxDataViewColumn::SetTitle(const wxString& title)
{
- // bitmaps and titles cannot exist at the same time - if the title is set the bitmap is removed:
+ // bitmaps and titles cannot exist at the same time - if the title is set
+ // the bitmap is removed:
wxDataViewColumnBase::SetBitmap(wxBitmap());
m_title = title;
[[m_NativeDataPtr->GetNativeColumnPtr() headerCell] setStringValue:[[wxCFStringRef(title).AsNSString() retain] autorelease]];
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))