@implementation CocoaListView : NSScrollView -(id) initWith:(phoenix::ListView&)listViewReference { if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0)]) { listView = &listViewReference; content = [[CocoaListViewContent alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)]; [self setDocumentView:content]; [self setBorderType:NSBezelBorder]; [self setHasVerticalScroller:YES]; [content setDataSource:self]; [content setDelegate:self]; [content setTarget:self]; [content setDoubleAction:@selector(doubleAction:)]; [content setAllowsColumnReordering:NO]; [content setAllowsColumnResizing:YES]; [content setAllowsColumnSelection:NO]; [content setAllowsEmptySelection:YES]; [content setAllowsMultipleSelection:NO]; [content setColumnAutoresizingStyle:NSTableViewLastColumnOnlyAutoresizingStyle]; font = nil; [self setFont:nil]; } return self; } -(void) dealloc { [content release]; [font release]; [super dealloc]; } -(CocoaListViewContent*) content { return content; } -(NSFont*) font { return font; } -(void) setFont:(NSFont*)fontPointer { if(!fontPointer) fontPointer = [NSFont systemFontOfSize:12]; [fontPointer retain]; if(font) [font release]; font = fontPointer; unsigned fontHeight = phoenix::pFont::size(font, " ").height; [content setFont:font]; [content setRowHeight:fontHeight]; [self reloadColumns]; } -(void) reloadColumns { while([[content tableColumns] count]) { [content removeTableColumn:[[content tableColumns] lastObject]]; } if(listView->state.checkable) { NSTableColumn *tableColumn = [[NSTableColumn alloc] initWithIdentifier:@"check"]; NSTableHeaderCell *headerCell = [[NSTableHeaderCell alloc] initTextCell:@""]; NSButtonCell *dataCell = [[NSButtonCell alloc] initTextCell:@""]; [dataCell setButtonType:NSSwitchButton]; [dataCell setControlSize:NSSmallControlSize]; [dataCell setRefusesFirstResponder:YES]; [tableColumn setResizingMask:NSTableColumnNoResizing]; [tableColumn setHeaderCell:headerCell]; [tableColumn setDataCell:dataCell]; [tableColumn setWidth:20.0]; [content addTableColumn:tableColumn]; } lstring headers = listView->state.headerText; if(headers.size() == 0) headers.append(""); [content setUsesAlternatingRowBackgroundColors:headers.size() >= 2]; for(unsigned column = 0; column < headers.size(); column++) { NSTableColumn* tableColumn = [[NSTableColumn alloc] initWithIdentifier:[[NSNumber numberWithInteger:column] stringValue]]; NSTableHeaderCell* headerCell = [[NSTableHeaderCell alloc] initTextCell:[NSString stringWithUTF8String:headers(column)]]; CocoaListViewCell* dataCell = [[CocoaListViewCell alloc] initTextCell:@""]; [dataCell setEditable:NO]; [tableColumn setResizingMask:NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask]; [tableColumn setHeaderCell:headerCell]; [tableColumn setDataCell:dataCell]; [content addTableColumn:tableColumn]; } } -(NSInteger) numberOfRowsInTableView:(NSTableView*)table { return listView->state.text.size(); } -(id) tableView:(NSTableView*)table objectValueForTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row { if([[tableColumn identifier] isEqualToString:@"check"]) { auto checked = listView->state.checked(row) ? NSOnState : NSOffState; return [NSNumber numberWithInteger:checked]; } NSInteger column = [[tableColumn identifier] integerValue]; unsigned height = [table rowHeight]; NSString* text = [NSString stringWithUTF8String:listView->state.text(row)(column)]; NSImage* image = NSMakeImage(listView->state.image(row)(column), height, height); if(image) return @{ @"text":text, @"image":image }; return @{ @"text":text }; } -(BOOL) tableView:(NSTableView*)table shouldShowCellExpansionForTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row { return NO; } -(NSString*) tableView:(NSTableView*)table toolTipForCell:(NSCell*)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row mouseLocation:(NSPoint)mouseLocation { return nil; } -(void) tableView:(NSTableView*)table setObjectValue:(id)object forTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row { if([[tableColumn identifier] isEqualToString:@"check"]) { listView->state.checked(row) = [object integerValue] != NSOffState; if(listView->onToggle) listView->onToggle(row); } } -(void) tableView:(NSTableView*)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row { [cell setFont:[self font]]; } -(void) tableViewSelectionDidChange:(NSNotification*)notification { listView->state.selected = true; listView->state.selection = [content selectedRow]; if(listView->onChange) listView->onChange(); } -(IBAction) activate:(id)sender { if(listView->onActivate) listView->onActivate(); } -(IBAction) doubleAction:(id)sender { if([content clickedRow] >= 0) { [self activate:self]; } } @end @implementation CocoaListViewContent : NSTableView -(void) keyDown:(NSEvent*)event { auto character = [[event characters] characterAtIndex:0]; if(character == NSEnterCharacter || character == NSCarriageReturnCharacter) { if([self selectedRow] >= 0) { [[self delegate] activate:self]; return; } } [super keyDown:event]; } @end @implementation CocoaListViewCell : NSTextFieldCell //used by type-ahead -(NSString*) stringValue { return [[self objectValue] objectForKey:@"text"]; } -(void) drawWithFrame:(NSRect)frame inView:(NSView*)view { NSString* text = [[self objectValue] objectForKey:@"text"]; NSImage* image = [[self objectValue] objectForKey:@"image"]; unsigned textDisplacement = 0; if(image) { [[NSGraphicsContext currentContext] saveGraphicsState]; NSRect targetRect = NSMakeRect(frame.origin.x, frame.origin.y, frame.size.height, frame.size.height); NSRect sourceRect = NSMakeRect(0, 0, [image size].width, [image size].height); [image drawInRect:targetRect fromRect:sourceRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:YES hints:nil]; [[NSGraphicsContext currentContext] restoreGraphicsState]; textDisplacement = frame.size.height + 2; } NSRect textRect = NSMakeRect( frame.origin.x + textDisplacement, frame.origin.y, frame.size.width - textDisplacement, frame.size.height ); NSColor* textColor = [self isHighlighted] ? [NSColor alternateSelectedControlTextColor] : [NSColor textColor]; [text drawInRect:textRect withAttributes:@{ NSForegroundColorAttributeName:textColor, NSFontAttributeName:[self font] }]; } @end namespace phoenix { void pListView::append(const lstring& text) { @autoreleasepool { [[cocoaView content] reloadData]; } } void pListView::autoSizeColumns() { @autoreleasepool { if(listView.state.checkable) { NSTableColumn* tableColumn = [[cocoaView content] tableColumnWithIdentifier:@"check"]; [tableColumn setWidth:20.0]; } unsigned height = [[cocoaView content] rowHeight]; for(unsigned column = 0; column < max(1u, listView.state.headerText.size()); column++) { NSTableColumn* tableColumn = [[cocoaView content] tableColumnWithIdentifier:[[NSNumber numberWithInteger:column] stringValue]]; unsigned minimumWidth = pFont::size([[tableColumn headerCell] font], listView.state.headerText(column)).width + 4; for(unsigned row = 0; row < listView.state.text.size(); row++) { unsigned width = pFont::size([cocoaView font], listView.state.text(row)(column)).width + 2; if(listView.state.image(row)(height).empty() == false) width += height + 2; if(width > minimumWidth) minimumWidth = width; } [tableColumn setWidth:minimumWidth]; } [[cocoaView content] sizeLastColumnToFit]; } } void pListView::remove(unsigned selection) { @autoreleasepool { [[cocoaView content] reloadData]; } } void pListView::reset() { @autoreleasepool { [[cocoaView content] reloadData]; } } void pListView::setBackgroundColor(Color color) { } void pListView::setCheckable(bool checkable) { @autoreleasepool { [cocoaView reloadColumns]; } } void pListView::setChecked(unsigned selection, bool checked) { @autoreleasepool { [[cocoaView content] reloadData]; } } void pListView::setFont(string font) { @autoreleasepool { [cocoaView setFont:pFont::cocoaFont(font)]; } } void pListView::setForegroundColor(Color color) { } void pListView::setHeaderText(const lstring& text) { @autoreleasepool { [cocoaView reloadColumns]; } } void pListView::setHeaderVisible(bool visible) { @autoreleasepool { if(visible) { [[cocoaView content] setHeaderView:[[[NSTableHeaderView alloc] init] autorelease]]; } else { [[cocoaView content] setHeaderView:nil]; } } } void pListView::setImage(unsigned selection, unsigned position, const image& image) { @autoreleasepool { [[cocoaView content] reloadData]; } } void pListView::setSelected(bool selected) { @autoreleasepool { if(selected == false) { [[cocoaView content] deselectAll:nil]; } } } void pListView::setSelection(unsigned selection) { @autoreleasepool { [[cocoaView content] selectRowIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(selection, 1)] byExtendingSelection:NO]; } } void pListView::setText(unsigned selection, unsigned position, const string text) { @autoreleasepool { [[cocoaView content] reloadData]; } } void pListView::constructor() { @autoreleasepool { cocoaView = cocoaListView = [[CocoaListView alloc] initWith:listView]; setHeaderVisible(listView.state.headerVisible); setHeaderText(listView.state.headerText); } } void pListView::destructor() { @autoreleasepool { [cocoaView release]; } } }