Rationale
=========

I've got a beef with GnomeDateEdit. Actually not a beef, but a whole
cattle-herd. I will go into detail later. To overcome my problems with
GnomeDateEdit, I have to reimplement the widget for my GNOME 2 port
(basically a reimplementation) of GNOME-PIM. But since I hate code
reduplication, I'm offering to discuss a reimplementation of
GnomeDateEdit in either libgnomeui or in gtk+ (where it does belong,
IMO).

For the sake of argument I'm going to call the reimplementation
GpwDateTime (gpw = GNOME-PIM Widget, don't ask...), but think of it
as if I was proposing a GnomeDateTime or GtkDateTime widget.

(See also http://bugzilla.gnome.org/show_bug.cgi?id=52372 for a list
of other issues with GnomeDateTime.)

The Cattle-Herd
===============

Let's go through the current API of GnomeDateEdit step by step:

| GtkWidget *gnome_date_edit_new            (time_t the_time,
|                                            gboolean show_time,
|                                            gboolean use_24_format);
| GtkWidget *gnome_date_edit_new_flags      (time_t the_time,
|                                            GnomeDateEditFlags flags);
| void      gnome_date_edit_construct       (GnomeDateEdit *gde,
|                                            time_t the_time,
|                                            GnomeDateEditFlags flags);

The constructors. GnomeDateEditFlags is:

| typedef enum {
|         GNOME_DATE_EDIT_SHOW_TIME             = 1 << 0,
|         GNOME_DATE_EDIT_24_HR                 = 1 << 1,
|         GNOME_DATE_EDIT_WEEK_STARTS_ON_MONDAY = 1 << 2
| } GnomeDateEditFlags;

With other words: each application has to know whether the user wishes
to display his/her dates in 24 hour format and if his/her week starts
on monday. This means that every application has to have its own
settings and the user has to set it anew in every application.

One workaround to this problem would be to have a system-wide location
for GConf keys that store this information. But this would lead to a
lot of code-duplication, nevertheless, since every application that
uses a GnomeDateEdit has to query this key and has to react to changes
to it.

What's really needed is a locale-dependent default or - if that is not
possible - a global per-user setting. This can then be overridden by
the application with special accessor methods. But normally this
wouldn't be needed.

| void      gnome_date_edit_set_time        (GnomeDateEdit *gde, time_t the_time);
| time_t    gnome_date_edit_get_time        (GnomeDateEdit *gde);

Nothing wrong with these methods per se. But they are the only methods
to set/get the time (except the constructors)! This does mean that this
widget is limited API-wise to dates inside the Unix time, and so is
not feasible for many real-world applications. (GNOME-PIM example:
birthdays)

| void      gnome_date_edit_set_popup_range (GnomeDateEdit *gde, int low_hour, int up_hour);

Ok.

| void      gnome_date_edit_set_flags       (GnomeDateEdit *gde, GnomeDateEditFlags flags);
| int       gnome_date_edit_get_flags       (GnomeDateEdit *gde);

A very un-gtk-ish way of doing things. If fact to set a certain flag,
you have to perform two steps:

GnomeDateEditFlags *flags;
flags  = gnome_date_edit_get_flags (gde);
flags |= my_set_flags;
flags &= ~my_unset_flags;
gnome_date_edit_set_flags (gde, flags);

Ugly.

| time_t    gnome_date_edit_get_initial_time(GnomeDateEdit *gde);

What's this for? This is the only widget I know of that contains some
kind of "undo" capability.

Also please note that while it's possible to show only the date entry
or the date entry together with a time entry, it's not possible to
only show the time entry.

Proposed API
============

This is the new API that I'm planning to use for GpwDateTime and which
I propose for a new GnomeDateTime/GtkDateTime widget as well. Comments
are very welcome. (Actually that's the whole point, why I'm posting
this here.)

| GtkWidget *gpw_date_time_new (void);

Default constructor. Defaults to the current time and showing only the
date portion of the widget.

| GtkWidget *gpw_date_time_new_from_time_t (time_t *t);
| GtkWidget *gpw_date_time_new_from_struct_tm (struct tm *tm);
| GtkWidget *gpw_date_time_new_from_gdate (GDate *date);
| GtkWidget *gpw_date_time_new_from_time (gushort hour, gushort minute gushort second);

Convenience constructors.

| void     gpw_date_time_set_from_time_t    (GpwDateTime *gdt, time_t *t);
| gboolean gpw_date_time_get_as_time_t      (GpwDateTime *gdt, time_t *t);
| void     gpw_date_time_set_from_struct_tm (GpwDateTime *gdt, struct tm *tm);
| gboolean gpw_date_time_get_as_struct_tm   (GpwDateTime *gdt, struct tm *tm);
| void     gpw_date_time_set_from_gdate     (GpwDateTime *gdt, GDate *date);
| gboolean gpw_date_time_get_as_gdate       (GpwDateTime *gdt, GDate *date);
| void     gpw_date_time_set_time           (GpwDateTime *gdt, gushort hour, gushort minute, gushort second);
| gboolean gpw_date_time_get_time           (GowDateTime *gdt, gushort *hour, gushort *minute, gushort *second);

The gpw_date_time_get_* methods will fill in the supplied pointer and
return TRUE if the widget has a date/time set and it is valid, and
FALSE otherwise. (See below as well.) Also, the structure will be set
to be invalid in the former case, i.e. a time_t will be set to -1,
a struct tm and a GDate to an invalid value.

Now for the "flags" part:

| void     gpw_date_time_set_empty_allowed (GpwDateTime *gdt, gboolean allowed);
| gboolean gpw_date_time_get_empty_allowed (GpwDateTime *gdt);

Defaults to FALSE. If this is set, the date entry can be empty, meaning
"I don't know". Useful for example for birthdays or other optional dates.
This will be accompanied in the API docs with a warning that it's often
better to use a toggle widget to disable the date widget.

| typedef _GpwDateTimeDisplayMode {
|         GPW_DATE_TIME_DISPLAY_DATE, /* default */
|         GPW_DATE_TIME_DISPLAY_TIME,
|         GPW_DATE_TIME_DISPLAY_DATE_TIME
| } GpwDateTimeDisplayMode;
| 
| void     gpw_date_time_set_display_mode (GpwDateTime *gdt, GpwDateTimeDisplayMode mode);
| GpwDateTimeDisplayMode
|          gpw_date_time_get_display_mode (GpwDateTime *gdt);

Defaults to GPW_DATE_TIME_DISPLAY_DATE.

| void     gpw_date_time_set_show_seconds (GpwDateTime *gdt, gboolean show_seconds);
| gboolean gpw_date_time_get_show_seconds (GpwDateTime *gdt);

| void     gpw_date_time_set_24_hour (GpwDateTime *gdt, gboolean 24hour);
| gboolean gpw_date_time_get_24_hour (GpwDateTime *gdt);
| void     gpw_date_time_set_week_starts_monday (GpwDateTime *gdt, gboolean week_starts_monday);
| gboolean gpw_date_time_get_week_starts_monday (GpwDateTime *gdt);

Defaults are locale-dependant.

Not implemented is popup_range. Should I consider it?

Conclusion
==========

So, now it's your turn to make comments. I would be really interested to
get this widget in one of the future version of either libgnomeui or
(preferably) gtk+. Opinions?

 - Sebastian
