diff --git a/redo/objc_darwin.h b/redo/objc_darwin.h index eef1501..f07180d 100644 --- a/redo/objc_darwin.h +++ b/redo/objc_darwin.h @@ -86,9 +86,10 @@ extern struct xsize tabPreferredSize(id); enum { colTypeText, colTypeImage, + colTypeCheckbox, }; extern id newTable(void); -extern void tableAppendColumn(id, intptr_t, char *, int); +extern void tableAppendColumn(id, intptr_t, char *, int, BOOL); extern void tableUpdate(id); extern void tableMakeDataSource(id, void *); extern struct xsize tablePreferredSize(id); diff --git a/redo/table_darwin.go b/redo/table_darwin.go index 662b2ee..1f889c0 100644 --- a/redo/table_darwin.go +++ b/redo/table_darwin.go @@ -31,11 +31,15 @@ func finishNewTable(b *tablebase, ty reflect.Type) Table { for i := 0; i < ty.NumField(); i++ { cname := C.CString(ty.Field(i).Name) coltype := C.colTypeText - switch ty.Field(i).Type { - case reflect.TypeOf(ImageIndex(0)): + editable := false + switch { + case ty.Field(i).Type == reflect.TypeOf(ImageIndex(0)): coltype = C.colTypeImage + case ty.Field(i).Type.Kind() == reflect.Bool: + coltype = C.colTypeCheckbox + editable = true } - C.tableAppendColumn(t._id, C.intptr_t(i), cname, C.int(coltype)) + C.tableAppendColumn(t._id, C.intptr_t(i), cname, C.int(coltype), toBOOL(editable)) C.free(unsafe.Pointer(cname)) // free now (not deferred) to conserve memory } return t @@ -59,16 +63,25 @@ func (t *table) LoadImageList(i ImageList) { } //export goTableDataSource_getValue -func goTableDataSource_getValue(data unsafe.Pointer, row C.intptr_t, col C.intptr_t, isObject *C.BOOL) unsafe.Pointer { +func goTableDataSource_getValue(data unsafe.Pointer, row C.intptr_t, col C.intptr_t, outtype *C.int) unsafe.Pointer { t := (*table)(data) t.RLock() defer t.RUnlock() d := reflect.Indirect(reflect.ValueOf(t.data)) datum := d.Index(int(row)).Field(int(col)) - switch d := datum.Interface().(type) { - case ImageIndex: - *isObject = C.YES + switch { + case datum.Type() == reflect.TypeOf(ImageIndex(0)): + *outtype = C.colTypeImage + d := datum.Interface().(ImageIndex) return unsafe.Pointer(t.images[d]) + case datum.Kind() == reflect.Bool: + *outtype = C.colTypeCheckbox + if datum.Bool() == true { + // return a non-nil pointer + // outtype isn't Go-side so it'll work + return unsafe.Pointer(outtype) + } + return nil default: s := fmt.Sprintf("%v", datum) return unsafe.Pointer(C.CString(s)) @@ -84,6 +97,16 @@ func goTableDataSource_getRowCount(data unsafe.Pointer) C.intptr_t { return C.intptr_t(d.Len()) } +//export goTableDataSource_toggled +func goTableDataSource_toggled(data unsafe.Pointer, row C.intptr_t, col C.intptr_t, checked C.BOOL) { + t := (*table)(data) + t.Lock() + defer t.Unlock() + d := reflect.Indirect(reflect.ValueOf(t.data)) + datum := d.Index(int(row)).Field(int(col)) + datum.SetBool(fromBOOL(checked)) +} + func (t *table) id() C.id { return t._id } diff --git a/redo/table_darwin.m b/redo/table_darwin.m index ab66682..e3c2192 100644 --- a/redo/table_darwin.m +++ b/redo/table_darwin.m @@ -36,18 +36,33 @@ NSString *s; intptr_t colnum; char *str; - BOOL isObject = FALSE; + int type = colTypeText; colnum = ((goTableColumn *) col)->gocolnum; - ret = goTableDataSource_getValue(self->gotable, (intptr_t) row, colnum, &isObject); - if (isObject) + ret = goTableDataSource_getValue(self->gotable, (intptr_t) row, colnum, &type); + switch (type) { + case colTypeImage: return (id) ret; + case colTypeCheckbox: + if (ret == NULL) + return nil; + return (id) [NSNumber numberWithInt:1]; + } str = (char *) ret; s = [NSString stringWithUTF8String:str]; free(str); // allocated with C.CString() on the Go side return (id) s; } +- (void)tableView:(NSTableView *)view setObjectValue:(id)value forTableColumn:(NSTableColumn *)col row:(NSInteger)row +{ + intptr_t colnum; + + colnum = ((goTableColumn *) col)->gocolnum; + // TODO verify type of value + goTableDataSource_toggled(self->gotable, (intptr_t) row, colnum, [value boolValue]); +} + @end id newTable(void) @@ -63,10 +78,12 @@ id newTable(void) return (id) t; } -void tableAppendColumn(id t, intptr_t colnum, char *name, int type) +void tableAppendColumn(id t, intptr_t colnum, char *name, int type, BOOL editable) { goTableColumn *c; NSImageCell *ic; + NSButtonCell *bc; + NSLineBreakMode lbm = NSLineBreakByTruncatingTail; // default for most types c = [[goTableColumn alloc] initWithIdentifier:nil]; c->gocolnum = colnum; @@ -80,9 +97,20 @@ void tableAppendColumn(id t, intptr_t colnum, char *name, int type) [ic setImageAlignment:NSImageAlignCenter]; [c setDataCell:ic]; break; + case colTypeCheckbox: + bc = [[NSButtonCell alloc] init]; + [bc setBezelStyle:NSRegularSquareBezelStyle]; // not explicitly stated as such in Interface Builder; extracted with a test program + [bc setButtonType:NSSwitchButton]; + [bc setBordered:NO]; + [bc setTransparent:NO]; + [bc setAllowsMixedState:NO]; + [bc setTitle:@""]; // no label + lbm = NSLineBreakByWordWrapping; // Interface Builder sets this mode for this type + [c setDataCell:bc]; + break; } // otherwise just use the current cell - [c setEditable:NO]; + [c setEditable:editable]; [[c headerCell] setStringValue:[NSString stringWithUTF8String:name]]; setSmallControlFont((id) [c headerCell]); setStandardControlFont((id) [c dataCell]); @@ -95,7 +123,7 @@ void tableAppendColumn(id t, intptr_t colnum, char *name, int type) [[c headerCell] setTruncatesLastVisibleLine:NO]; [[c dataCell] setScrollable:NO]; [[c dataCell] setWraps:NO]; - [[c dataCell] setLineBreakMode:NSLineBreakByTruncatingTail]; + [[c dataCell] setLineBreakMode:lbm]; [[c dataCell] setUsesSingleLineMode:NO]; [[c dataCell] setTruncatesLastVisibleLine:NO]; [toNSTableView(t) addTableColumn:c];