Merge branch 'master' into table
This commit is contained in:
commit
9dad806693
|
@ -27,6 +27,7 @@ set(MSVC_INCREMENTAL_DEFAULT ON)
|
||||||
|
|
||||||
# default to debug builds
|
# default to debug builds
|
||||||
# do this before project() just to be safe
|
# do this before project() just to be safe
|
||||||
|
# either DEBUG or Debug will work; we use DEBUG as that's what cmake does internally (https://cmake.org/pipermail/cmake/2013-June/055177.html)
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE DEBUG CACHE STRING "" FORCE)
|
set(CMAKE_BUILD_TYPE DEBUG CACHE STRING "" FORCE)
|
||||||
endif()
|
endif()
|
||||||
|
@ -75,13 +76,14 @@ if(MSVC)
|
||||||
# TODO /sdl turns C4996 into an ERROR
|
# TODO /sdl turns C4996 into an ERROR
|
||||||
# don't use /analyze; that requires us to write annotations everywhere
|
# don't use /analyze; that requires us to write annotations everywhere
|
||||||
# TODO undecided flags from qo?
|
# TODO undecided flags from qo?
|
||||||
|
# the RTC flags are only supplied in debug builds because they are only supposed to be used by debug builds (see "This is because run-time error checks are not valid in a release (optimized) build." in https://docs.microsoft.com/cpp/build/reference/rtc-run-time-error-checks)
|
||||||
# /RTCc is not supplied because it's discouraged as of VS2015; see https://www.reddit.com/r/cpp/comments/46mhne/rtcc_rejects_conformant_code_with_visual_c_2015/d06auq5
|
# /RTCc is not supplied because it's discouraged as of VS2015; see https://www.reddit.com/r/cpp/comments/46mhne/rtcc_rejects_conformant_code_with_visual_c_2015/d06auq5
|
||||||
# /EHsc is to shut the compiler up in some cases
|
# /EHsc is to shut the compiler up in some cases
|
||||||
# TODO make /EHsc C++-only
|
# TODO make /EHsc C++-only
|
||||||
set(_COMMON_CFLAGS
|
set(_COMMON_CFLAGS
|
||||||
/W4 /wd4100
|
/W4 /wd4100
|
||||||
/bigobj /nologo
|
/bigobj /nologo
|
||||||
/RTC1 /RTCs /RTCu
|
$<$<CONFIG:Debug>:/RTC1 /RTCs /RTCu>
|
||||||
/EHsc
|
/EHsc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,9 @@ This README is being written.<br>
|
||||||
|
|
||||||
## Announcements
|
## Announcements
|
||||||
|
|
||||||
|
* **13 May 2018**
|
||||||
|
* Added new functions to work with uiDateTimePickers: `uiDateTimePickerTime()`, `uiDateTimePickerSetTime()`, and `uiDateTimePickerOnChanged()`. These operate on standard `<time.h>` `struct tm`s. Thanks @cody271!
|
||||||
|
|
||||||
* **2 May 2018**
|
* **2 May 2018**
|
||||||
* On Windows, you no longer need to carry around a `libui.res` file with static builds. You do need to link in the appropriate manifest file, such as the one in the `windows/` folder (I still need to figure out exactly what is needed apart from the Common Controls v6 dependency, or at least to create a complete-ish template), or at least include it alongside your executables. This also means you should no longer see random cmake errors when building the static libraries.
|
* On Windows, you no longer need to carry around a `libui.res` file with static builds. You do need to link in the appropriate manifest file, such as the one in the `windows/` folder (I still need to figure out exactly what is needed apart from the Common Controls v6 dependency, or at least to create a complete-ish template), or at least include it alongside your executables. This also means you should no longer see random cmake errors when building the static libraries.
|
||||||
|
|
||||||
|
@ -58,6 +61,9 @@ This README is being written.<br>
|
||||||
|
|
||||||
*Note that today's entry (Eastern Time) may be updated later today.*
|
*Note that today's entry (Eastern Time) may be updated later today.*
|
||||||
|
|
||||||
|
* **13 May 2018**
|
||||||
|
* Release builds on Windows with MSVC should be fixed now; thanks @l0calh05t, @slahn, @mischnic, and @zentner-kyle.
|
||||||
|
|
||||||
* **12 May 2018**
|
* **12 May 2018**
|
||||||
* GTK+ and OS X now have a cleaner build process for static libraries which no longer has intermediate files and differing configurations. As a result, certain issues should no longer be present. New naming rules for internal symbols of libui have also started being drafted; runtime symbols and edge cases still need to be handled (and the rules applied to Windows) before this can become a regular thing.
|
* GTK+ and OS X now have a cleaner build process for static libraries which no longer has intermediate files and differing configurations. As a result, certain issues should no longer be present. New naming rules for internal symbols of libui have also started being drafted; runtime symbols and edge cases still need to be handled (and the rules applied to Windows) before this can become a regular thing.
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,116 @@
|
||||||
struct uiDateTimePicker {
|
struct uiDateTimePicker {
|
||||||
uiDarwinControl c;
|
uiDarwinControl c;
|
||||||
NSDatePicker *dp;
|
NSDatePicker *dp;
|
||||||
|
void (*onChanged)(uiDateTimePicker *, void *);
|
||||||
|
void *onChangedData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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<NSDatePickerCellDelegate> {
|
||||||
|
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 *)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];
|
||||||
|
(*(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)
|
uiDarwinControlAllDefaults(uiDateTimePicker, dp)
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
[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)
|
static uiDateTimePicker *finishNewDateTimePicker(NSDatePickerElementFlags elements)
|
||||||
{
|
{
|
||||||
uiDateTimePicker *d;
|
uiDateTimePicker *d;
|
||||||
|
@ -15,6 +121,7 @@ static uiDateTimePicker *finishNewDateTimePicker(NSDatePickerElementFlags elemen
|
||||||
uiDarwinNewControl(uiDateTimePicker, d);
|
uiDarwinNewControl(uiDateTimePicker, d);
|
||||||
|
|
||||||
d->dp = [[NSDatePicker alloc] initWithFrame:NSZeroRect];
|
d->dp = [[NSDatePicker alloc] initWithFrame:NSZeroRect];
|
||||||
|
[d->dp setDateValue:[NSDate date]];
|
||||||
[d->dp setBordered:NO];
|
[d->dp setBordered:NO];
|
||||||
[d->dp setBezeled:YES];
|
[d->dp setBezeled:YES];
|
||||||
[d->dp setDrawsBackground:YES];
|
[d->dp setDrawsBackground:YES];
|
||||||
|
@ -23,6 +130,13 @@ static uiDateTimePicker *finishNewDateTimePicker(NSDatePickerElementFlags elemen
|
||||||
[d->dp setDatePickerMode:NSSingleDateMode];
|
[d->dp setDatePickerMode:NSSingleDateMode];
|
||||||
uiDarwinSetControlFont(d->dp, NSRegularControlSize);
|
uiDarwinSetControlFont(d->dp, NSRegularControlSize);
|
||||||
|
|
||||||
|
if (datePickerDelegate == nil) {
|
||||||
|
datePickerDelegate = [[uiprivDatePickerDelegateClass new] autorelease];
|
||||||
|
[uiprivDelegates addObject:datePickerDelegate];
|
||||||
|
}
|
||||||
|
[datePickerDelegate registerPicker:d];
|
||||||
|
uiDateTimePickerOnChanged(d, defaultOnChanged, NULL);
|
||||||
|
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,10 +47,16 @@ _add_example(timer
|
||||||
${_EXAMPLE_RESOURCES_RC}
|
${_EXAMPLE_RESOURCES_RC}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_add_example(datetime
|
||||||
|
datetime/main.c
|
||||||
|
${_EXAMPLE_RESOURCES_RC}
|
||||||
|
)
|
||||||
|
|
||||||
add_custom_target(examples
|
add_custom_target(examples
|
||||||
DEPENDS
|
DEPENDS
|
||||||
controlgallery
|
controlgallery
|
||||||
histogram
|
histogram
|
||||||
cpp-multithread
|
cpp-multithread
|
||||||
drawtext
|
drawtext
|
||||||
timer)
|
timer
|
||||||
|
datetime)
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
#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;
|
||||||
|
const char *err;
|
||||||
|
uiWindow *w;
|
||||||
|
uiGrid *g;
|
||||||
|
uiLabel *l;
|
||||||
|
uiButton *b;
|
||||||
|
|
||||||
|
memset(&o, 0, sizeof (uiInitOptions));
|
||||||
|
err = uiInit(&o);
|
||||||
|
if (err != NULL) {
|
||||||
|
fprintf(stderr, "error initializing ui: %s\n", err);
|
||||||
|
uiFreeInitError(err);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
5
ui.h
|
@ -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 void uiRadioButtonsOnSelected(uiRadioButtons *r, void (*f)(uiRadioButtons *, void *), void *data);
|
||||||
_UI_EXTERN uiRadioButtons *uiNewRadioButtons(void);
|
_UI_EXTERN uiRadioButtons *uiNewRadioButtons(void);
|
||||||
|
|
||||||
|
struct tm;
|
||||||
typedef struct uiDateTimePicker uiDateTimePicker;
|
typedef struct uiDateTimePicker uiDateTimePicker;
|
||||||
#define uiDateTimePicker(this) ((uiDateTimePicker *) (this))
|
#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 *uiNewDateTimePicker(void);
|
||||||
_UI_EXTERN uiDateTimePicker *uiNewDatePicker(void);
|
_UI_EXTERN uiDateTimePicker *uiNewDatePicker(void);
|
||||||
_UI_EXTERN uiDateTimePicker *uiNewTimePicker(void);
|
_UI_EXTERN uiDateTimePicker *uiNewTimePicker(void);
|
||||||
|
|
|
@ -4,17 +4,17 @@
|
||||||
// LONGTERM imitate gnome-calendar's day/month/year entries above the calendar
|
// 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
|
// LONGTERM allow entering a 24-hour hour in the hour spinbutton and adjust accordingly
|
||||||
|
|
||||||
#define dateTimePickerWidgetType (dateTimePickerWidget_get_type())
|
#define uiprivDateTimePickerWidgetType (uiprivDateTimePickerWidget_get_type())
|
||||||
#define dateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), dateTimePickerWidgetType, dateTimePickerWidget))
|
#define uiprivDateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), uiprivDateTimePickerWidgetType, uiprivDateTimePickerWidget))
|
||||||
#define isDateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), dateTimePickerWidgetType))
|
#define isDateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), uiprivDateTimePickerWidgetType))
|
||||||
#define dateTimePickerWidgetClass(class) (G_TYPE_CHECK_CLASS_CAST((class), dateTimePickerWidgetType, dateTimePickerWidgetClass))
|
#define uiprivDateTimePickerWidgetClass(class) (G_TYPE_CHECK_CLASS_CAST((class), uiprivDateTimePickerWidgetType, uiprivDateTimePickerWidgetClass))
|
||||||
#define isDateTimePickerWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), dateTimePickerWidget))
|
#define isDateTimePickerWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), uiprivDateTimePickerWidget))
|
||||||
#define getDateTimePickerWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), dateTimePickerWidgetType, dateTimePickerWidgetClass))
|
#define getDateTimePickerWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), uiprivDateTimePickerWidgetType, uiprivDateTimePickerWidgetClass))
|
||||||
|
|
||||||
typedef struct dateTimePickerWidget dateTimePickerWidget;
|
typedef struct uiprivDateTimePickerWidget uiprivDateTimePickerWidget;
|
||||||
typedef struct dateTimePickerWidgetClass dateTimePickerWidgetClass;
|
typedef struct uiprivDateTimePickerWidgetClass uiprivDateTimePickerWidgetClass;
|
||||||
|
|
||||||
struct dateTimePickerWidget {
|
struct uiprivDateTimePickerWidget {
|
||||||
GtkToggleButton parent_instance;
|
GtkToggleButton parent_instance;
|
||||||
|
|
||||||
gulong toggledSignal;
|
gulong toggledSignal;
|
||||||
|
@ -31,6 +31,7 @@ struct dateTimePickerWidget {
|
||||||
GtkWidget *seconds;
|
GtkWidget *seconds;
|
||||||
GtkWidget *ampm;
|
GtkWidget *ampm;
|
||||||
|
|
||||||
|
gulong calendarBlock;
|
||||||
gulong hoursBlock;
|
gulong hoursBlock;
|
||||||
gulong minutesBlock;
|
gulong minutesBlock;
|
||||||
gulong secondsBlock;
|
gulong secondsBlock;
|
||||||
|
@ -40,11 +41,11 @@ struct dateTimePickerWidget {
|
||||||
GdkDevice *mouse;
|
GdkDevice *mouse;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dateTimePickerWidgetClass {
|
struct uiprivDateTimePickerWidgetClass {
|
||||||
GtkToggleButtonClass parent_class;
|
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)
|
static int realSpinValue(GtkSpinButton *spinButton)
|
||||||
{
|
{
|
||||||
|
@ -64,7 +65,7 @@ static void setRealSpinValue(GtkSpinButton *spinButton, int value, gulong block)
|
||||||
g_signal_handler_unblock(spinButton, 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
|
// 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;
|
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);
|
return g_date_time_new_local(year, month, day, hour, minute, second);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setLabel(dateTimePickerWidget *d)
|
static void setLabel(uiprivDateTimePickerWidget *d)
|
||||||
{
|
{
|
||||||
GDateTime *dt;
|
GDateTime *dt;
|
||||||
char *fmt;
|
char *fmt;
|
||||||
|
@ -109,14 +110,17 @@ static void setLabel(dateTimePickerWidget *d)
|
||||||
g_date_time_unref(dt);
|
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);
|
setLabel(d);
|
||||||
// TODO fire event here
|
// TODO fire event here instead?
|
||||||
}
|
}
|
||||||
|
|
||||||
// we don't want ::toggled to be sent again
|
// 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);
|
g_signal_handler_block(d, d->toggledSignal);
|
||||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d), active);
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d), active);
|
||||||
|
@ -124,7 +128,7 @@ static void setActive(dateTimePickerWidget *d, gboolean active)
|
||||||
}
|
}
|
||||||
|
|
||||||
// like startGrab() below, a lot of this is in the order that GtkComboBox does it
|
// 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)
|
if (d->keyboard != NULL)
|
||||||
gdk_device_ungrab(d->keyboard, GDK_CURRENT_TIME);
|
gdk_device_ungrab(d->keyboard, GDK_CURRENT_TIME);
|
||||||
|
@ -134,7 +138,7 @@ static void endGrab(dateTimePickerWidget *d)
|
||||||
d->mouse = NULL;
|
d->mouse = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hidePopup(dateTimePickerWidget *d)
|
static void hidePopup(uiprivDateTimePickerWidget *d)
|
||||||
{
|
{
|
||||||
endGrab(d);
|
endGrab(d);
|
||||||
gtk_widget_hide(d->window);
|
gtk_widget_hide(d->window);
|
||||||
|
@ -142,7 +146,7 @@ static void hidePopup(dateTimePickerWidget *d)
|
||||||
}
|
}
|
||||||
|
|
||||||
// this consolidates a good chunk of what GtkComboBox does
|
// this consolidates a good chunk of what GtkComboBox does
|
||||||
static gboolean startGrab(dateTimePickerWidget *d)
|
static gboolean startGrab(uiprivDateTimePickerWidget *d)
|
||||||
{
|
{
|
||||||
GdkDevice *dev;
|
GdkDevice *dev;
|
||||||
guint32 time;
|
guint32 time;
|
||||||
|
@ -197,7 +201,7 @@ static gboolean startGrab(dateTimePickerWidget *d)
|
||||||
}
|
}
|
||||||
|
|
||||||
// based on gtk_combo_box_list_position() in the GTK+ source code
|
// 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;
|
GdkWindow *window;
|
||||||
GtkAllocation a;
|
GtkAllocation a;
|
||||||
|
@ -237,7 +241,7 @@ static void allocationToScreen(dateTimePickerWidget *d, gint *x, gint *y)
|
||||||
*y = otherY;
|
*y = otherY;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void showPopup(dateTimePickerWidget *d)
|
static void showPopup(uiprivDateTimePickerWidget *d)
|
||||||
{
|
{
|
||||||
GtkWidget *toplevel;
|
GtkWidget *toplevel;
|
||||||
gint x, y;
|
gint x, y;
|
||||||
|
@ -259,7 +263,7 @@ static void showPopup(dateTimePickerWidget *d)
|
||||||
|
|
||||||
static void onToggled(GtkToggleButton *b, gpointer data)
|
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)))
|
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d)))
|
||||||
showPopup(d);
|
showPopup(d);
|
||||||
|
@ -269,7 +273,7 @@ static void onToggled(GtkToggleButton *b, gpointer data)
|
||||||
|
|
||||||
static gboolean grabBroken(GtkWidget *w, GdkEventGrabBroken *e, gpointer data)
|
static gboolean grabBroken(GtkWidget *w, GdkEventGrabBroken *e, gpointer data)
|
||||||
{
|
{
|
||||||
dateTimePickerWidget *d = dateTimePickerWidget(data);
|
uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(data);
|
||||||
|
|
||||||
hidePopup(d);
|
hidePopup(d);
|
||||||
return TRUE; // this is what GtkComboBox does
|
return TRUE; // this is what GtkComboBox does
|
||||||
|
@ -277,7 +281,7 @@ static gboolean grabBroken(GtkWidget *w, GdkEventGrabBroken *e, gpointer data)
|
||||||
|
|
||||||
static gboolean buttonReleased(GtkWidget *w, GdkEventButton *e, gpointer data)
|
static gboolean buttonReleased(GtkWidget *w, GdkEventButton *e, gpointer data)
|
||||||
{
|
{
|
||||||
dateTimePickerWidget *d = dateTimePickerWidget(data);
|
uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(data);
|
||||||
int winx, winy;
|
int winx, winy;
|
||||||
GtkAllocation wina;
|
GtkAllocation wina;
|
||||||
gboolean in;
|
gboolean in;
|
||||||
|
@ -382,12 +386,12 @@ static gboolean ampmSpinboxOutput(GtkSpinButton *sb, gpointer data)
|
||||||
|
|
||||||
static void spinboxChanged(GtkSpinButton *sb, gpointer data)
|
static void spinboxChanged(GtkSpinButton *sb, gpointer data)
|
||||||
{
|
{
|
||||||
dateTimePickerWidget *d = dateTimePickerWidget(data);
|
uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(data);
|
||||||
|
|
||||||
dateTimeChanged(d);
|
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;
|
GtkWidget *sb;
|
||||||
|
|
||||||
|
@ -405,30 +409,52 @@ static GtkWidget *newSpinbox(dateTimePickerWidget *d, int min, int max, gint (*i
|
||||||
|
|
||||||
static void dateChanged(GtkCalendar *c, gpointer data)
|
static void dateChanged(GtkCalendar *c, gpointer data)
|
||||||
{
|
{
|
||||||
dateTimePickerWidget *d = dateTimePickerWidget(data);
|
uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(data);
|
||||||
|
|
||||||
dateTimeChanged(d);
|
dateTimeChanged(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setDateOnly(dateTimePickerWidget *d)
|
static void setDateOnly(uiprivDateTimePickerWidget *d)
|
||||||
{
|
{
|
||||||
d->hasTime = FALSE;
|
d->hasTime = FALSE;
|
||||||
gtk_container_remove(GTK_CONTAINER(d->box), d->timebox);
|
gtk_container_remove(GTK_CONTAINER(d->box), d->timebox);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setTimeOnly(dateTimePickerWidget *d)
|
static void setTimeOnly(uiprivDateTimePickerWidget *d)
|
||||||
{
|
{
|
||||||
d->hasDate = FALSE;
|
d->hasDate = FALSE;
|
||||||
gtk_container_remove(GTK_CONTAINER(d->box), d->calendar);
|
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 year, month, day;
|
||||||
gint hour;
|
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);
|
d->window = gtk_window_new(GTK_WINDOW_POPUP);
|
||||||
gtk_window_set_resizable(GTK_WINDOW(d->window), FALSE);
|
gtk_window_set_resizable(GTK_WINDOW(d->window), FALSE);
|
||||||
gtk_window_set_attached_to(GTK_WINDOW(d->window), GTK_WIDGET(d));
|
gtk_window_set_attached_to(GTK_WINDOW(d->window), GTK_WIDGET(d));
|
||||||
|
@ -446,7 +472,7 @@ static void dateTimePickerWidget_init(dateTimePickerWidget *d)
|
||||||
gtk_container_add(GTK_CONTAINER(d->window), d->box);
|
gtk_container_add(GTK_CONTAINER(d->window), d->box);
|
||||||
|
|
||||||
d->calendar = gtk_calendar_new();
|
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);
|
gtk_container_add(GTK_CONTAINER(d->box), d->calendar);
|
||||||
|
|
||||||
d->timebox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
d->timebox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
||||||
|
@ -493,53 +519,103 @@ static void dateTimePickerWidget_init(dateTimePickerWidget *d)
|
||||||
d->hasDate = TRUE;
|
d->hasDate = TRUE;
|
||||||
|
|
||||||
// set the current date/time
|
// set the current date/time
|
||||||
// notice how we block signals from firing
|
uiprivDateTimePickerWidget_setTime(d, g_date_time_new_now_local());
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dateTimePickerWidget_dispose(GObject *obj)
|
static void uiprivDateTimePickerWidget_dispose(GObject *obj)
|
||||||
{
|
{
|
||||||
dateTimePickerWidget *d = dateTimePickerWidget(obj);
|
uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(obj);
|
||||||
|
|
||||||
if (d->window != NULL) {
|
if (d->window != NULL) {
|
||||||
gtk_widget_destroy(d->window);
|
gtk_widget_destroy(d->window);
|
||||||
d->window = NULL;
|
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)->dispose = uiprivDateTimePickerWidget_dispose;
|
||||||
G_OBJECT_CLASS(class)->finalize = dateTimePickerWidget_finalize;
|
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)
|
static GtkWidget *newDTP(void)
|
||||||
{
|
{
|
||||||
GtkWidget *w;
|
GtkWidget *w;
|
||||||
|
|
||||||
w = GTK_WIDGET(g_object_new(dateTimePickerWidgetType, "label", "", NULL));
|
w = GTK_WIDGET(g_object_new(uiprivDateTimePickerWidgetType, "label", "", NULL));
|
||||||
setLabel(dateTimePickerWidget(w));
|
setLabel(uiprivDateTimePickerWidget(w));
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,9 +623,9 @@ static GtkWidget *newDP(void)
|
||||||
{
|
{
|
||||||
GtkWidget *w;
|
GtkWidget *w;
|
||||||
|
|
||||||
w = GTK_WIDGET(g_object_new(dateTimePickerWidgetType, "label", "", NULL));
|
w = GTK_WIDGET(g_object_new(uiprivDateTimePickerWidgetType, "label", "", NULL));
|
||||||
setDateOnly(dateTimePickerWidget(w));
|
setDateOnly(uiprivDateTimePickerWidget(w));
|
||||||
setLabel(dateTimePickerWidget(w));
|
setLabel(uiprivDateTimePickerWidget(w));
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,20 +633,12 @@ static GtkWidget *newTP(void)
|
||||||
{
|
{
|
||||||
GtkWidget *w;
|
GtkWidget *w;
|
||||||
|
|
||||||
w = GTK_WIDGET(g_object_new(dateTimePickerWidgetType, "label", "", NULL));
|
w = GTK_WIDGET(g_object_new(uiprivDateTimePickerWidgetType, "label", "", NULL));
|
||||||
setTimeOnly(dateTimePickerWidget(w));
|
setTimeOnly(uiprivDateTimePickerWidget(w));
|
||||||
setLabel(dateTimePickerWidget(w));
|
setLabel(uiprivDateTimePickerWidget(w));
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct uiDateTimePicker {
|
|
||||||
uiUnixControl c;
|
|
||||||
GtkWidget *widget;
|
|
||||||
dateTimePickerWidget *d;
|
|
||||||
};
|
|
||||||
|
|
||||||
uiUnixControlAllDefaults(uiDateTimePicker)
|
|
||||||
|
|
||||||
uiDateTimePicker *finishNewDateTimePicker(GtkWidget *(*fn)(void))
|
uiDateTimePicker *finishNewDateTimePicker(GtkWidget *(*fn)(void))
|
||||||
{
|
{
|
||||||
uiDateTimePicker *d;
|
uiDateTimePicker *d;
|
||||||
|
@ -578,7 +646,9 @@ uiDateTimePicker *finishNewDateTimePicker(GtkWidget *(*fn)(void))
|
||||||
uiUnixNewControl(uiDateTimePicker, d);
|
uiUnixNewControl(uiDateTimePicker, d);
|
||||||
|
|
||||||
d->widget = (*fn)();
|
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;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
struct uiDateTimePicker {
|
struct uiDateTimePicker {
|
||||||
uiWindowsControl c;
|
uiWindowsControl c;
|
||||||
HWND hwnd;
|
HWND hwnd;
|
||||||
|
void (*onChanged)(uiDateTimePicker *, void *);
|
||||||
|
void *onChangedData;
|
||||||
};
|
};
|
||||||
|
|
||||||
// utility functions
|
// utility functions
|
||||||
|
@ -103,6 +105,7 @@ static void uiDateTimePickerDestroy(uiControl *c)
|
||||||
uiDateTimePicker *d = uiDateTimePicker(c);
|
uiDateTimePicker *d = uiDateTimePicker(c);
|
||||||
|
|
||||||
uiWindowsUnregisterReceiveWM_WININICHANGE(d->hwnd);
|
uiWindowsUnregisterReceiveWM_WININICHANGE(d->hwnd);
|
||||||
|
uiWindowsUnregisterWM_NOTIFYHandler(d->hwnd);
|
||||||
uiWindowsEnsureDestroyWindow(d->hwnd);
|
uiWindowsEnsureDestroyWindow(d->hwnd);
|
||||||
uiFreeControl(uiControl(d));
|
uiFreeControl(uiControl(d));
|
||||||
}
|
}
|
||||||
|
@ -131,6 +134,71 @@ static void uiDateTimePickerMinimumSize(uiWindowsControl *c, int *width, int *he
|
||||||
*height = y;
|
*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");
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data)
|
||||||
|
{
|
||||||
|
d->onChanged = f;
|
||||||
|
d->onChangedData = data;
|
||||||
|
}
|
||||||
|
|
||||||
static uiDateTimePicker *finishNewDateTimePicker(DWORD style)
|
static uiDateTimePicker *finishNewDateTimePicker(DWORD style)
|
||||||
{
|
{
|
||||||
uiDateTimePicker *d;
|
uiDateTimePicker *d;
|
||||||
|
@ -147,6 +215,8 @@ static uiDateTimePicker *finishNewDateTimePicker(DWORD style)
|
||||||
// for the standard styles, this is in the date-time picker itself
|
// 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()
|
// for our date/time mode, we do it in a subclass assigned in uiNewDateTimePicker()
|
||||||
uiWindowsRegisterReceiveWM_WININICHANGE(d->hwnd);
|
uiWindowsRegisterReceiveWM_WININICHANGE(d->hwnd);
|
||||||
|
uiWindowsRegisterWM_NOTIFYHandler(d->hwnd, onWM_NOTIFY, uiControl(d));
|
||||||
|
uiDateTimePickerOnChanged(d, defaultOnChanged, NULL);
|
||||||
|
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue