// 14 august 2015 #import "uipriv_darwin.h" struct uiDateTimePicker { uiDarwinControl c; NSDatePicker *dp; void (*onChanged)(uiDateTimePicker *, void *); void *onChangedData; BOOL blockSendOnce; }; // TODO see if target-action works here or not; I forgot what cody271@ originally said // the primary advantage of the delegate is the ability to reject changes, but libui doesn't support that yet — we should consider that API option as well @interface uiprivDatePickerDelegateClass : NSObject { uiprivMap *pickers; } - (void)datePickerCell:(NSDatePickerCell *)aDatePickerCell validateProposedDateValue:(NSDate **)proposedDateValue timeInterval:(NSTimeInterval *)proposedTimeInterval; - (void)doTimer:(NSTimer *)timer; - (void)registerPicker:(uiDateTimePicker *)d; - (void)unregisterPicker:(uiDateTimePicker *)d; @end @implementation uiprivDatePickerDelegateClass - (id)init { self = [super init]; if (self) self->pickers = uiprivNewMap(); return self; } - (void)dealloc { uiprivMapDestroy(self->pickers); [super dealloc]; } - (void)datePickerCell:(NSDatePickerCell *)cell validateProposedDateValue:(NSDate **)proposedDateValue timeInterval:(NSTimeInterval *)proposedTimeInterval { uiDateTimePicker *d; d = (uiDateTimePicker *) uiprivMapGet(self->pickers, cell); [NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(doTimer:) userInfo:[NSValue valueWithPointer:d] repeats:NO]; } - (void)doTimer:(NSTimer *)timer { NSValue *v; uiDateTimePicker *d; v = (NSValue *) [timer userInfo]; d = (uiDateTimePicker *) [v pointerValue]; if (d->blockSendOnce) { d->blockSendOnce = NO; return; } (*(d->onChanged))(d, d->onChangedData); } - (void)registerPicker:(uiDateTimePicker *)d { uiprivMapSet(self->pickers, d->dp.cell, d); [d->dp setDelegate:self]; } - (void)unregisterPicker:(uiDateTimePicker *)d { [d->dp setDelegate:nil]; uiprivMapDelete(self->pickers, d->dp.cell); } @end static uiprivDatePickerDelegateClass *datePickerDelegate = nil; uiDarwinControlAllDefaultsExceptDestroy(uiDateTimePicker, dp) static void uiDateTimePickerDestroy(uiControl *c) { uiDateTimePicker *d = uiDateTimePicker(c); [datePickerDelegate unregisterPicker:d]; [d->dp release]; uiFreeControl(uiControl(d)); } static void defaultOnChanged(uiDateTimePicker *d, void *data) { // do nothing } // TODO consider using NSDateComponents iff we ever need the extra accuracy of not using NSTimeInterval void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) { time_t t; struct tm tmbuf; NSDate *date; date = [d->dp dateValue]; t = (time_t) [date timeIntervalSince1970]; // Copy time to minimize a race condition // time.h functions use global non-thread-safe data tmbuf = *localtime(&t); memcpy(time, &tmbuf, sizeof (struct tm)); } void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) { time_t t; struct tm tmbuf; // Copy time because mktime() modifies its argument memcpy(&tmbuf, time, sizeof (struct tm)); t = mktime(&tmbuf); // TODO get rid of the need for this d->blockSendOnce = YES; [d->dp setDateValue:[NSDate dateWithTimeIntervalSince1970:t]]; } void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data) { d->onChanged = f; d->onChangedData = data; } static uiDateTimePicker *finishNewDateTimePicker(NSDatePickerElementFlags elements) { uiDateTimePicker *d; uiDarwinNewControl(uiDateTimePicker, d); d->dp = [[NSDatePicker alloc] initWithFrame:NSZeroRect]; [d->dp setDateValue:[NSDate date]]; [d->dp setBordered:NO]; [d->dp setBezeled:YES]; [d->dp setDrawsBackground:YES]; [d->dp setDatePickerStyle:NSTextFieldAndStepperDatePickerStyle]; [d->dp setDatePickerElements:elements]; [d->dp setDatePickerMode:NSSingleDateMode]; uiDarwinSetControlFont(d->dp, NSRegularControlSize); if (datePickerDelegate == nil) { datePickerDelegate = [[uiprivDatePickerDelegateClass new] autorelease]; [uiprivDelegates addObject:datePickerDelegate]; } [datePickerDelegate registerPicker:d]; uiDateTimePickerOnChanged(d, defaultOnChanged, NULL); return d; } uiDateTimePicker *uiNewDateTimePicker(void) { return finishNewDateTimePicker(NSYearMonthDayDatePickerElementFlag | NSHourMinuteSecondDatePickerElementFlag); } uiDateTimePicker *uiNewDatePicker(void) { return finishNewDateTimePicker(NSYearMonthDayDatePickerElementFlag); } uiDateTimePicker *uiNewTimePicker(void) { return finishNewDateTimePicker(NSHourMinuteSecondDatePickerElementFlag); }