Merge pull request #297 from cody271/datetime

Improve uiDateTimePicker by adding functions to set and get the displayed date.

Updates #309
This commit is contained in:
Pietro Gagliardi 2018-05-13 11:22:44 -04:00 committed by GitHub
commit d02784838e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 460 additions and 75 deletions

View File

@ -4,10 +4,113 @@
struct uiDateTimePicker {
uiDarwinControl c;
NSDatePicker *dp;
void (*onChanged)(uiDateTimePicker *, void *);
void *onChangedData;
};
@interface uiprivDatePickerDelegateClass : NSObject <NSDatePickerCellDelegate> {
struct uiprivMap *pickers;
}
- (void)datePickerCell:(NSDatePickerCell *)aDatePickerCell validateProposedDateValue:(NSDate **)proposedDateValue timeInterval:(NSTimeInterval *)proposedTimeInterval;
- (void)doTimer:(NSTimer *)timer;
- (void)registerPicker:(uiDateTimePicker *)b;
- (void)unregisterPicker:(uiDateTimePicker *)b;
@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 *)aDatePickerCell
validateProposedDateValue:(NSDate **)proposedDateValue
timeInterval:(NSTimeInterval *)proposedTimeInterval
{
uiDateTimePicker *d;
d = (uiDateTimePicker *) uiprivMapGet(self->pickers, aDatePickerCell);
[NSTimer scheduledTimerWithTimeInterval:0
target:self
selector:@selector(doTimer:)
userInfo:[NSValue valueWithPointer:d]
repeats:NO];
}
- (void)doTimer:(NSTimer *)timer
{
uiDateTimePicker *d;
d = (uiDateTimePicker *) [((NSValue *)[timer userInfo]) pointerValue];
(*(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;
uiDarwinControlAllDefaults(uiDateTimePicker, dp)
static void defaultOnChanged(uiDateTimePicker *d, void *data)
{
// do nothing
}
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);
[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;
@ -15,6 +118,7 @@ static uiDateTimePicker *finishNewDateTimePicker(NSDatePickerElementFlags elemen
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];
@ -23,6 +127,13 @@ static uiDateTimePicker *finishNewDateTimePicker(NSDatePickerElementFlags elemen
[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;
}

View File

@ -23,6 +23,11 @@ _add_example(histogram
${_EXAMPLE_RESOURCES_RC}
)
_add_example(datetime
datetime/main.c
${_EXAMPLE_RESOURCES_RC}
)
_add_example(cpp-multithread
cpp-multithread/main.cpp
${_EXAMPLE_RESOURCES_RC}
@ -53,4 +58,5 @@ add_custom_target(examples
histogram
cpp-multithread
drawtext
datetime
timer)

123
examples/datetime/main.c Normal file
View File

@ -0,0 +1,123 @@
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "../../ui.h"
uiDateTimePicker *dtboth, *dtdate, *dttime;
const char *timeFormat(uiDateTimePicker *d)
{
const char *fmt;
if (d == dtboth)
fmt = "%c";
else if (d == dtdate)
fmt = "%x";
else if (d == dttime)
fmt = "%X";
else
fmt = "";
return fmt;
}
void onChanged(uiDateTimePicker *d, void *data)
{
struct tm time;
char buf[64];
uiDateTimePickerTime(d, &time);
strftime(buf, sizeof (buf), timeFormat(d), &time);
uiLabelSetText(uiLabel(data), buf);
}
void onClicked(uiButton *b, void *data)
{
intptr_t now;
time_t t;
struct tm tmbuf;
now = (intptr_t) data;
t = 0;
if (now)
t = time(NULL);
tmbuf = *localtime(&t);
if (now) {
uiDateTimePickerSetTime(dtdate, &tmbuf);
uiDateTimePickerSetTime(dttime, &tmbuf);
} else
uiDateTimePickerSetTime(dtboth, &tmbuf);
}
int onClosing(uiWindow *w, void *data)
{
uiQuit();
return 1;
}
int main(void)
{
uiInitOptions o;
uiWindow *w;
uiGrid *g;
uiLabel *l;
uiButton *b;
memset(&o, 0, sizeof (uiInitOptions));
if (uiInit(&o) != NULL)
abort();
w = uiNewWindow("Date / Time", 320, 240, 0);
uiWindowSetMargined(w, 1);
g = uiNewGrid();
uiGridSetPadded(g, 1);
uiWindowSetChild(w, uiControl(g));
dtboth = uiNewDateTimePicker();
dtdate = uiNewDatePicker();
dttime = uiNewTimePicker();
uiGridAppend(g, uiControl(dtboth),
0, 0, 2, 1,
1, uiAlignFill, 0, uiAlignFill);
uiGridAppend(g, uiControl(dtdate),
0, 1, 1, 1,
1, uiAlignFill, 0, uiAlignFill);
uiGridAppend(g, uiControl(dttime),
1, 1, 1, 1,
1, uiAlignFill, 0, uiAlignFill);
l = uiNewLabel("");
uiGridAppend(g, uiControl(l),
0, 2, 2, 1,
1, uiAlignCenter, 0, uiAlignFill);
uiDateTimePickerOnChanged(dtboth, onChanged, l);
l = uiNewLabel("");
uiGridAppend(g, uiControl(l),
0, 3, 1, 1,
1, uiAlignCenter, 0, uiAlignFill);
uiDateTimePickerOnChanged(dtdate, onChanged, l);
l = uiNewLabel("");
uiGridAppend(g, uiControl(l),
1, 3, 1, 1,
1, uiAlignCenter, 0, uiAlignFill);
uiDateTimePickerOnChanged(dttime, onChanged, l);
b = uiNewButton("Now");
uiButtonOnClicked(b, onClicked, (void *) 1);
uiGridAppend(g, uiControl(b),
0, 4, 1, 1,
1, uiAlignFill, 1, uiAlignEnd);
b = uiNewButton("Unix epoch");
uiButtonOnClicked(b, onClicked, (void *) 0);
uiGridAppend(g, uiControl(b),
1, 4, 1, 1,
1, uiAlignFill, 1, uiAlignEnd);
uiWindowOnClosing(w, onClosing, NULL);
uiControlShow(uiControl(w));
uiMain();
return 0;
}

5
ui.h
View File

@ -248,8 +248,13 @@ _UI_EXTERN void uiRadioButtonsSetSelected(uiRadioButtons *r, int n);
_UI_EXTERN void uiRadioButtonsOnSelected(uiRadioButtons *r, void (*f)(uiRadioButtons *, void *), void *data);
_UI_EXTERN uiRadioButtons *uiNewRadioButtons(void);
struct tm;
typedef struct uiDateTimePicker uiDateTimePicker;
#define uiDateTimePicker(this) ((uiDateTimePicker *) (this))
// TODO document that tm_wday and tm_yday are undefined, and tm_isdst should be -1
_UI_EXTERN void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time);
_UI_EXTERN void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time);
_UI_EXTERN void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data);
_UI_EXTERN uiDateTimePicker *uiNewDateTimePicker(void);
_UI_EXTERN uiDateTimePicker *uiNewDatePicker(void);
_UI_EXTERN uiDateTimePicker *uiNewTimePicker(void);

View File

@ -4,17 +4,17 @@
// LONGTERM imitate gnome-calendar's day/month/year entries above the calendar
// LONGTERM allow entering a 24-hour hour in the hour spinbutton and adjust accordingly
#define dateTimePickerWidgetType (dateTimePickerWidget_get_type())
#define dateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), dateTimePickerWidgetType, dateTimePickerWidget))
#define isDateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), dateTimePickerWidgetType))
#define dateTimePickerWidgetClass(class) (G_TYPE_CHECK_CLASS_CAST((class), dateTimePickerWidgetType, dateTimePickerWidgetClass))
#define isDateTimePickerWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), dateTimePickerWidget))
#define getDateTimePickerWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), dateTimePickerWidgetType, dateTimePickerWidgetClass))
#define uiprivDateTimePickerWidgetType (uiprivDateTimePickerWidget_get_type())
#define uiprivDateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), uiprivDateTimePickerWidgetType, uiprivDateTimePickerWidget))
#define isDateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), uiprivDateTimePickerWidgetType))
#define uiprivDateTimePickerWidgetClass(class) (G_TYPE_CHECK_CLASS_CAST((class), uiprivDateTimePickerWidgetType, uiprivDateTimePickerWidgetClass))
#define isDateTimePickerWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), uiprivDateTimePickerWidget))
#define getDateTimePickerWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), uiprivDateTimePickerWidgetType, uiprivDateTimePickerWidgetClass))
typedef struct dateTimePickerWidget dateTimePickerWidget;
typedef struct dateTimePickerWidgetClass dateTimePickerWidgetClass;
typedef struct uiprivDateTimePickerWidget uiprivDateTimePickerWidget;
typedef struct uiprivDateTimePickerWidgetClass uiprivDateTimePickerWidgetClass;
struct dateTimePickerWidget {
struct uiprivDateTimePickerWidget {
GtkToggleButton parent_instance;
gulong toggledSignal;
@ -31,6 +31,7 @@ struct dateTimePickerWidget {
GtkWidget *seconds;
GtkWidget *ampm;
gulong calendarBlock;
gulong hoursBlock;
gulong minutesBlock;
gulong secondsBlock;
@ -40,11 +41,11 @@ struct dateTimePickerWidget {
GdkDevice *mouse;
};
struct dateTimePickerWidgetClass {
struct uiprivDateTimePickerWidgetClass {
GtkToggleButtonClass parent_class;
};
G_DEFINE_TYPE(dateTimePickerWidget, dateTimePickerWidget, GTK_TYPE_TOGGLE_BUTTON)
G_DEFINE_TYPE(uiprivDateTimePickerWidget, uiprivDateTimePickerWidget, GTK_TYPE_TOGGLE_BUTTON)
static int realSpinValue(GtkSpinButton *spinButton)
{
@ -64,7 +65,7 @@ static void setRealSpinValue(GtkSpinButton *spinButton, int value, gulong block)
g_signal_handler_unblock(spinButton, block);
}
static GDateTime *selected(dateTimePickerWidget *d)
static GDateTime *selected(uiprivDateTimePickerWidget *d)
{
// choose a day for which all times are likely to be valid for the default date in case we're only dealing with time
guint year = 1970, month = 1, day = 1;
@ -84,7 +85,7 @@ static GDateTime *selected(dateTimePickerWidget *d)
return g_date_time_new_local(year, month, day, hour, minute, second);
}
static void setLabel(dateTimePickerWidget *d)
static void setLabel(uiprivDateTimePickerWidget *d)
{
GDateTime *dt;
char *fmt;
@ -109,14 +110,16 @@ static void setLabel(dateTimePickerWidget *d)
g_date_time_unref(dt);
}
static void dateTimeChanged(dateTimePickerWidget *d)
static int changedSignal;
static void dateTimeChanged(uiprivDateTimePickerWidget *d)
{
g_signal_emit(d, changedSignal, 0);
setLabel(d);
// TODO fire event here
}
// we don't want ::toggled to be sent again
static void setActive(dateTimePickerWidget *d, gboolean active)
static void setActive(uiprivDateTimePickerWidget *d, gboolean active)
{
g_signal_handler_block(d, d->toggledSignal);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d), active);
@ -124,7 +127,7 @@ static void setActive(dateTimePickerWidget *d, gboolean active)
}
// like startGrab() below, a lot of this is in the order that GtkComboBox does it
static void endGrab(dateTimePickerWidget *d)
static void endGrab(uiprivDateTimePickerWidget *d)
{
if (d->keyboard != NULL)
gdk_device_ungrab(d->keyboard, GDK_CURRENT_TIME);
@ -134,7 +137,7 @@ static void endGrab(dateTimePickerWidget *d)
d->mouse = NULL;
}
static void hidePopup(dateTimePickerWidget *d)
static void hidePopup(uiprivDateTimePickerWidget *d)
{
endGrab(d);
gtk_widget_hide(d->window);
@ -142,7 +145,7 @@ static void hidePopup(dateTimePickerWidget *d)
}
// this consolidates a good chunk of what GtkComboBox does
static gboolean startGrab(dateTimePickerWidget *d)
static gboolean startGrab(uiprivDateTimePickerWidget *d)
{
GdkDevice *dev;
guint32 time;
@ -197,7 +200,7 @@ static gboolean startGrab(dateTimePickerWidget *d)
}
// based on gtk_combo_box_list_position() in the GTK+ source code
static void allocationToScreen(dateTimePickerWidget *d, gint *x, gint *y)
static void allocationToScreen(uiprivDateTimePickerWidget *d, gint *x, gint *y)
{
GdkWindow *window;
GtkAllocation a;
@ -237,7 +240,7 @@ static void allocationToScreen(dateTimePickerWidget *d, gint *x, gint *y)
*y = otherY;
}
static void showPopup(dateTimePickerWidget *d)
static void showPopup(uiprivDateTimePickerWidget *d)
{
GtkWidget *toplevel;
gint x, y;
@ -259,7 +262,7 @@ static void showPopup(dateTimePickerWidget *d)
static void onToggled(GtkToggleButton *b, gpointer data)
{
dateTimePickerWidget *d = dateTimePickerWidget(b);
uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(b);
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d)))
showPopup(d);
@ -269,7 +272,7 @@ static void onToggled(GtkToggleButton *b, gpointer data)
static gboolean grabBroken(GtkWidget *w, GdkEventGrabBroken *e, gpointer data)
{
dateTimePickerWidget *d = dateTimePickerWidget(data);
uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(data);
hidePopup(d);
return TRUE; // this is what GtkComboBox does
@ -277,7 +280,7 @@ static gboolean grabBroken(GtkWidget *w, GdkEventGrabBroken *e, gpointer data)
static gboolean buttonReleased(GtkWidget *w, GdkEventButton *e, gpointer data)
{
dateTimePickerWidget *d = dateTimePickerWidget(data);
uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(data);
int winx, winy;
GtkAllocation wina;
gboolean in;
@ -382,12 +385,12 @@ static gboolean ampmSpinboxOutput(GtkSpinButton *sb, gpointer data)
static void spinboxChanged(GtkSpinButton *sb, gpointer data)
{
dateTimePickerWidget *d = dateTimePickerWidget(data);
uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(data);
dateTimeChanged(d);
}
static GtkWidget *newSpinbox(dateTimePickerWidget *d, int min, int max, gint (*input)(GtkSpinButton *, gpointer, gpointer), gboolean (*output)(GtkSpinButton *, gpointer), gulong *block)
static GtkWidget *newSpinbox(uiprivDateTimePickerWidget *d, int min, int max, gint (*input)(GtkSpinButton *, gpointer, gpointer), gboolean (*output)(GtkSpinButton *, gpointer), gulong *block)
{
GtkWidget *sb;
@ -405,30 +408,52 @@ static GtkWidget *newSpinbox(dateTimePickerWidget *d, int min, int max, gint (*i
static void dateChanged(GtkCalendar *c, gpointer data)
{
dateTimePickerWidget *d = dateTimePickerWidget(data);
uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(data);
dateTimeChanged(d);
}
static void setDateOnly(dateTimePickerWidget *d)
static void setDateOnly(uiprivDateTimePickerWidget *d)
{
d->hasTime = FALSE;
gtk_container_remove(GTK_CONTAINER(d->box), d->timebox);
}
static void setTimeOnly(dateTimePickerWidget *d)
static void setTimeOnly(uiprivDateTimePickerWidget *d)
{
d->hasDate = FALSE;
gtk_container_remove(GTK_CONTAINER(d->box), d->calendar);
}
static void dateTimePickerWidget_init(dateTimePickerWidget *d)
static void uiprivDateTimePickerWidget_setTime(uiprivDateTimePickerWidget *d, GDateTime *dt)
{
GDateTime *dt;
gint year, month, day;
gint hour;
gulong calendarBlock;
// notice how we block signals from firing
if (d->hasDate) {
g_date_time_get_ymd(dt, &year, &month, &day);
month--; // GDateTime/GtkCalendar differences
g_signal_handler_block(d->calendar, d->calendarBlock);
gtk_calendar_select_month(GTK_CALENDAR(d->calendar), month, year);
gtk_calendar_select_day(GTK_CALENDAR(d->calendar), day);
g_signal_handler_unblock(d->calendar, d->calendarBlock);
}
if (d->hasTime) {
hour = g_date_time_get_hour(dt);
if (hour >= 12) {
hour -= 12;
setRealSpinValue(GTK_SPIN_BUTTON(d->ampm), 1, d->ampmBlock);
}
setRealSpinValue(GTK_SPIN_BUTTON(d->hours), hour, d->hoursBlock);
setRealSpinValue(GTK_SPIN_BUTTON(d->minutes), g_date_time_get_minute(dt), d->minutesBlock);
setRealSpinValue(GTK_SPIN_BUTTON(d->seconds), g_date_time_get_seconds(dt), d->secondsBlock);
}
g_date_time_unref(dt);
}
static void uiprivDateTimePickerWidget_init(uiprivDateTimePickerWidget *d)
{
d->window = gtk_window_new(GTK_WINDOW_POPUP);
gtk_window_set_resizable(GTK_WINDOW(d->window), FALSE);
gtk_window_set_attached_to(GTK_WINDOW(d->window), GTK_WIDGET(d));
@ -446,7 +471,7 @@ static void dateTimePickerWidget_init(dateTimePickerWidget *d)
gtk_container_add(GTK_CONTAINER(d->window), d->box);
d->calendar = gtk_calendar_new();
calendarBlock = g_signal_connect(d->calendar, "day-selected", G_CALLBACK(dateChanged), d);
d->calendarBlock = g_signal_connect(d->calendar, "day-selected", G_CALLBACK(dateChanged), d);
gtk_container_add(GTK_CONTAINER(d->box), d->calendar);
d->timebox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
@ -493,53 +518,103 @@ static void dateTimePickerWidget_init(dateTimePickerWidget *d)
d->hasDate = TRUE;
// set the current date/time
// notice how we block signals from firing
dt = g_date_time_new_now_local();
g_date_time_get_ymd(dt, &year, &month, &day);
month--; // GDateTime/GtkCalendar differences
g_signal_handler_block(d->calendar, calendarBlock);
gtk_calendar_select_month(GTK_CALENDAR(d->calendar), month, year);
gtk_calendar_select_day(GTK_CALENDAR(d->calendar), day);
g_signal_handler_unblock(d->calendar, calendarBlock);
hour = g_date_time_get_hour(dt);
if (hour >= 12) {
hour -= 12;
setRealSpinValue(GTK_SPIN_BUTTON(d->ampm), 1, d->ampmBlock);
}
setRealSpinValue(GTK_SPIN_BUTTON(d->hours), hour, d->hoursBlock);
setRealSpinValue(GTK_SPIN_BUTTON(d->minutes), g_date_time_get_minute(dt), d->minutesBlock);
setRealSpinValue(GTK_SPIN_BUTTON(d->seconds), g_date_time_get_seconds(dt), d->secondsBlock);
g_date_time_unref(dt);
uiprivDateTimePickerWidget_setTime(d, g_date_time_new_now_local());
}
static void dateTimePickerWidget_dispose(GObject *obj)
static void uiprivDateTimePickerWidget_dispose(GObject *obj)
{
dateTimePickerWidget *d = dateTimePickerWidget(obj);
uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(obj);
if (d->window != NULL) {
gtk_widget_destroy(d->window);
d->window = NULL;
}
G_OBJECT_CLASS(dateTimePickerWidget_parent_class)->dispose(obj);
G_OBJECT_CLASS(uiprivDateTimePickerWidget_parent_class)->dispose(obj);
}
static void dateTimePickerWidget_finalize(GObject *obj)
static void uiprivDateTimePickerWidget_finalize(GObject *obj)
{
G_OBJECT_CLASS(dateTimePickerWidget_parent_class)->finalize(obj);
G_OBJECT_CLASS(uiprivDateTimePickerWidget_parent_class)->finalize(obj);
}
static void dateTimePickerWidget_class_init(dateTimePickerWidgetClass *class)
static void uiprivDateTimePickerWidget_class_init(uiprivDateTimePickerWidgetClass *class)
{
G_OBJECT_CLASS(class)->dispose = dateTimePickerWidget_dispose;
G_OBJECT_CLASS(class)->finalize = dateTimePickerWidget_finalize;
G_OBJECT_CLASS(class)->dispose = uiprivDateTimePickerWidget_dispose;
G_OBJECT_CLASS(class)->finalize = uiprivDateTimePickerWidget_finalize;
changedSignal = g_signal_new("changed",
G_TYPE_FROM_CLASS(class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
0);
}
struct uiDateTimePicker {
uiUnixControl c;
GtkWidget *widget;
uiprivDateTimePickerWidget *d;
void (*onChanged)(uiDateTimePicker *, void *);
void *onChangedData;
};
uiUnixControlAllDefaults(uiDateTimePicker)
static void defaultOnChanged(uiDateTimePicker *d, void *data)
{
// do nothing
}
void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time)
{
time_t t;
struct tm tmbuf;
GDateTime *dt;
dt = selected(d->d);
t = g_date_time_to_unix(dt);
g_date_time_unref(dt);
// 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);
uiprivDateTimePickerWidget_setTime(d->d, g_date_time_new_from_unix_local(t));
dateTimeChanged(d->d);
}
void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data)
{
d->onChanged = f;
d->onChangedData = data;
}
static void onChanged(uiprivDateTimePickerWidget *d, gpointer data)
{
uiDateTimePicker *c;
c = uiDateTimePicker(data);
(*(c->onChanged))(c, c->onChangedData);
}
static GtkWidget *newDTP(void)
{
GtkWidget *w;
w = GTK_WIDGET(g_object_new(dateTimePickerWidgetType, "label", "", NULL));
setLabel(dateTimePickerWidget(w));
w = GTK_WIDGET(g_object_new(uiprivDateTimePickerWidgetType, "label", "", NULL));
setLabel(uiprivDateTimePickerWidget(w));
return w;
}
@ -547,9 +622,9 @@ static GtkWidget *newDP(void)
{
GtkWidget *w;
w = GTK_WIDGET(g_object_new(dateTimePickerWidgetType, "label", "", NULL));
setDateOnly(dateTimePickerWidget(w));
setLabel(dateTimePickerWidget(w));
w = GTK_WIDGET(g_object_new(uiprivDateTimePickerWidgetType, "label", "", NULL));
setDateOnly(uiprivDateTimePickerWidget(w));
setLabel(uiprivDateTimePickerWidget(w));
return w;
}
@ -557,20 +632,12 @@ static GtkWidget *newTP(void)
{
GtkWidget *w;
w = GTK_WIDGET(g_object_new(dateTimePickerWidgetType, "label", "", NULL));
setTimeOnly(dateTimePickerWidget(w));
setLabel(dateTimePickerWidget(w));
w = GTK_WIDGET(g_object_new(uiprivDateTimePickerWidgetType, "label", "", NULL));
setTimeOnly(uiprivDateTimePickerWidget(w));
setLabel(uiprivDateTimePickerWidget(w));
return w;
}
struct uiDateTimePicker {
uiUnixControl c;
GtkWidget *widget;
dateTimePickerWidget *d;
};
uiUnixControlAllDefaults(uiDateTimePicker)
uiDateTimePicker *finishNewDateTimePicker(GtkWidget *(*fn)(void))
{
uiDateTimePicker *d;
@ -578,7 +645,9 @@ uiDateTimePicker *finishNewDateTimePicker(GtkWidget *(*fn)(void))
uiUnixNewControl(uiDateTimePicker, d);
d->widget = (*fn)();
d->d = dateTimePickerWidget(d->widget);
d->d = uiprivDateTimePickerWidget(d->widget);
g_signal_connect(d->widget, "changed", G_CALLBACK(onChanged), d);
uiDateTimePickerOnChanged(d, defaultOnChanged, NULL);
return d;
}

View File

@ -4,6 +4,8 @@
struct uiDateTimePicker {
uiWindowsControl c;
HWND hwnd;
void(*onChanged)(uiDateTimePicker *, void *);
void *onChangedData;
};
// utility functions
@ -103,6 +105,7 @@ static void uiDateTimePickerDestroy(uiControl *c)
uiDateTimePicker *d = uiDateTimePicker(c);
uiWindowsUnregisterReceiveWM_WININICHANGE(d->hwnd);
uiWindowsUnregisterWM_NOTIFYHandler(d->hwnd);
uiWindowsEnsureDestroyWindow(d->hwnd);
uiFreeControl(uiControl(d));
}
@ -131,6 +134,72 @@ static void uiDateTimePickerMinimumSize(uiWindowsControl *c, int *width, int *he
*height = y;
}
static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult)
{
uiDateTimePicker *d = uiDateTimePicker(c);
if (nmhdr->code != DTN_DATETIMECHANGE)
return FALSE;
(*(d->onChanged))(d, d->onChangedData);
*lResult = 0;
return TRUE;
}
static void fromSystemTime(SYSTEMTIME *systime, struct tm *time)
{
ZeroMemory(time, sizeof (struct tm));
time->tm_sec = systime->wSecond;
time->tm_min = systime->wMinute;
time->tm_hour = systime->wHour;
time->tm_mday = systime->wDay;
time->tm_mon = systime->wMonth - 1;
time->tm_year = systime->wYear - 1900;
time->tm_wday = systime->wDayOfWeek;
time->tm_isdst = -1;
}
static void toSystemTime(const struct tm *time, SYSTEMTIME *systime)
{
ZeroMemory(systime, sizeof (SYSTEMTIME));
systime->wYear = time->tm_year + 1900;
systime->wMonth = time->tm_mon + 1;
systime->wDayOfWeek = time->tm_wday;
systime->wDay = time->tm_mday;
systime->wHour = time->tm_hour;
systime->wMinute = time->tm_min;
systime->wSecond = time->tm_sec;
}
static void defaultOnChanged(uiDateTimePicker *d, void *data)
{
// do nothing
}
void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time)
{
SYSTEMTIME systime;
if (SendMessageW(d->hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM) (&systime)) != GDT_VALID)
logLastError(L"error getting date and time");
fromSystemTime(&systime, time);
}
void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time)
{
SYSTEMTIME systime;
toSystemTime(time, &systime);
if (SendMessageW(d->hwnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM) (&systime)) == 0)
logLastError(L"error setting date and time");
(*(d->onChanged))(d, d->onChangedData);
}
void uiDateTimePickerOnChanged(uiDateTimePicker *d, void(*f)(uiDateTimePicker *, void *), void *data)
{
d->onChanged = f;
d->onChangedData = data;
}
static uiDateTimePicker *finishNewDateTimePicker(DWORD style)
{
uiDateTimePicker *d;
@ -147,6 +216,8 @@ static uiDateTimePicker *finishNewDateTimePicker(DWORD style)
// for the standard styles, this is in the date-time picker itself
// for our date/time mode, we do it in a subclass assigned in uiNewDateTimePicker()
uiWindowsRegisterReceiveWM_WININICHANGE(d->hwnd);
uiWindowsRegisterWM_NOTIFYHandler(d->hwnd, onWM_NOTIFY, uiControl(d));
uiDateTimePickerOnChanged(d, defaultOnChanged, NULL);
return d;
}