Add a function for getting relative paths from full paths.
[anjuta-git-plugin.git] / plugins / gtodo / egg-datetime.c
blob81ea75b75051c0da15034ab32cdcd125b00ca35e
1 /*
2 * Copyright (C) 2002, 2003 Sebastian Rittau <srittau@jroger.in-berlin.de>
3 * $Id$
5 * Based on GnomeDateEdit by Miguel de Icaza.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with the Gnome Library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
22 /****************************************************************/
23 /* This file is modified for the use with gtodo. */
24 /* The following changes are made: */
25 /* - Fixed crashing when trying to set
26 a gdate withouth a valid dmy (but julian) */
27 /* - Made the entry box one char widther. this to fix that the text doesnt fit */
28 /* - Added a today and no_date button to the date popup */
29 /* And the needed interface for this. */
30 /* - The Date in the calendar is set tot the date represented by the datetime widget */
31 /****************************************************************/
35 #include <libintl.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <time.h>
41 #include <gdk/gdkkeysyms.h>
42 #include <gtk/gtk.h>
44 #include "egg-datetime.h"
45 #include "main.h"
48 /* TODO:
49 * o "now" button in calendar popup?
50 * o timezone support
51 * o Implement time list as a popup a la Evo, or time as a spin button?
52 * o In lazy mode: choose a different way to mark an invalid date instead
53 * of just blanking the widget.
56 /* FIXME:
57 * o limit GtkCalendar to clamp times
60 static void cal_set_today (EggDateTime *edt, GtkCalendar *calendar);
61 static void cal_set_nodate (EggDateTime *edt, GtkCalendar *calendar);
63 /* from libgnomeui */
64 /*static void
65 _add_atk_name_desc (GtkWidget *widget, gchar *name, gchar *desc)
67 AtkObject *aobj;
69 g_return_if_fail (GTK_IS_WIDGET (widget));
71 aobj = gtk_widget_get_accessible (widget);
73 if (name)
74 atk_object_set_name (aobj, name);
75 if (desc)
76 atk_object_set_description (aobj, desc);
79 /* This leaks.. and not little, so I remove this for now. */
82 static void
83 _add_atk_relation (GtkWidget *widget1, GtkWidget *widget2,
84 AtkRelationType w1_to_w2, AtkRelationType w2_to_w1)
86 AtkObject *atk_widget1;
87 AtkObject *atk_widget2;
88 AtkRelationSet *relation_set;
89 AtkRelation *relation;
90 AtkObject *targets[1];
92 atk_widget1 = gtk_widget_get_accessible (widget1);
93 atk_widget2 = gtk_widget_get_accessible (widget2);
95 /* Create the widget1 -> widget2 relation */
96 /* relation_set = atk_object_ref_relation_set (atk_widget1);
97 targets[0] = atk_widget2;
98 relation = atk_relation_new (targets, 1, w1_to_w2);
99 atk_relation_set_add (relation_set, relation);
100 g_object_unref (relation);
102 /* Create the widget2 -> widget1 relation */
103 /* relation_set = atk_object_ref_relation_set (atk_widget2);
104 targets[0] = atk_widget1;
105 relation = atk_relation_new (targets, 1, w2_to_w1);
106 atk_relation_set_add (relation_set, relation);
107 g_object_unref (relation);
111 * Time List Declarations
114 #define TIMELIST(x) GTK_SCROLLED_WINDOW(x)
115 #define Timelist GtkScrolledWindow
117 static GtkWidget *timelist_new (EggDateTime *edt);
118 static void timelist_set_list (Timelist *timelist, guint8 minhour, guint8 minminute, guint8 maxhour, guint8 maxminute);
119 static void timelist_set_time (Timelist *timelist, gint hour, gint minute);
120 static gboolean timelist_get_time (Timelist *timelist, gint *hour, gint *minute);
121 static void timelist_clamp (Timelist *timelist, guint8 minhour, guint8 minminute, guint8 maxhour, guint8 maxminute);
122 static void timelist_set_selection_callback (Timelist *timelist, void (*cb)(void), gpointer data);
125 * Class and Object Handling
128 struct _EggDateTimePrivate
130 /* Children */
132 GtkWidget *date_box;
133 GtkWidget *date_entry;
134 GtkWidget *date_button;
135 GtkWidget *time_box;
136 GtkWidget *time_entry;
137 GtkWidget *time_button;
138 GtkWidget *cal_popup;
139 GtkWidget *calendar;
140 GtkWidget *time_popup;
141 GtkWidget *timelist;
143 GtkWidget *today;
144 GtkWidget *nodate;
145 /* Flags */
147 EggDateTimeDisplayMode display_mode;
148 gboolean lazy;
149 gboolean week_start_monday;
151 /* Current Date/Time */
152 gboolean no_date;
153 gboolean date_valid;
154 GDateYear year;
155 GDateMonth month;
156 GDateDay day;
157 gboolean time_valid;
158 gint hour;
159 gint minute;
160 guint8 second;
162 /* Clamp Values */
164 guint16 clamp_minyear, clamp_maxyear;
165 guint8 clamp_minmonth, clamp_maxmonth;
166 guint8 clamp_minday, clamp_maxday;
167 guint8 clamp_minhour, clamp_maxhour;
168 guint8 clamp_minminute, clamp_maxminute;
169 guint8 clamp_minsecond, clamp_maxsecond;
172 enum {
173 SIGNAL_DATE_CHANGED,
174 SIGNAL_TIME_CHANGED,
175 SIGNAL_LAST
178 enum {
179 ARG_DISPLAY_MODE = 1,
180 ARG_LAZY,
181 ARG_YEAR,
182 ARG_MONTH,
183 ARG_DAY,
184 ARG_HOUR,
185 ARG_MINUTE,
186 ARG_SECOND,
187 ARG_CLAMP_MINYEAR,
188 ARG_CLAMP_MINMONTH,
189 ARG_CLAMP_MINDAY,
190 ARG_CLAMP_MINHOUR,
191 ARG_CLAMP_MINMINUTE,
192 ARG_CLAMP_MINSECOND,
193 ARG_CLAMP_MAXYEAR,
194 ARG_CLAMP_MAXMONTH,
195 ARG_CLAMP_MAXDAY,
196 ARG_CLAMP_MAXHOUR,
197 ARG_CLAMP_MAXMINUTE,
198 ARG_CLAMP_MAXSECOND
202 static gint egg_datetime_signals [SIGNAL_LAST] = { 0 };
205 static void egg_datetime_class_init (EggDateTimeClass *klass);
206 static void egg_datetime_init (EggDateTime *edt);
207 static void egg_datetime_set_property (GObject *object,
208 guint property_id,
209 const GValue *value,
210 GParamSpec *pspec);
211 static void egg_datetime_get_property (GObject *object,
212 guint property_id,
213 GValue *value,
214 GParamSpec *pspec);
216 static void egg_datetime_destroy (GtkObject *object);
217 static void egg_datetime_finalize (GObject *object);
219 static gchar *get_time_string (guint8 hour, guint8 minute, guint8 second);
221 static gboolean date_focus_out (EggDateTime *edt, GtkEntry *entry);
222 static gboolean time_focus_out (EggDateTime *edt, GtkEntry *entry);
223 static void date_arrow_toggled (EggDateTime *edt, GtkToggleButton *button);
224 static void time_arrow_toggled (EggDateTime *edt, GtkToggleButton *button);
225 static void cal_popup_changed (EggDateTime *edt, GtkCalendar *calendar);
226 static void cal_popup_double_click (EggDateTime *edt, GtkCalendar *calendar);
227 static gboolean cal_popup_key_pressed (EggDateTime *edt, GdkEventKey *event, GtkWidget *widget);
228 static gboolean cal_popup_button_pressed (EggDateTime *edt, GdkEventButton *event, GtkWidget *widget);
229 static gboolean cal_popup_closed (EggDateTime *edt, GtkWindow *popup);
230 static void cal_popup_hide (EggDateTime *edt);
231 static void time_popup_changed (EggDateTime *edt, Timelist *timelist);
232 static gboolean time_popup_key_pressed (EggDateTime *edt, GdkEventKey *event, GtkWidget *widget);
233 static gboolean time_popup_button_pressed (EggDateTime *edt, GdkEventButton *event, GtkWidget *widget);
234 static gboolean time_popup_closed (EggDateTime *edt, GtkWindow *popup);
235 static void time_popup_hide (EggDateTime *edt);
237 static void apply_display_mode (EggDateTime *edt);
238 static void clamp_time_labels (EggDateTime *edt);
239 static void parse_date (EggDateTime *edt);
240 static void parse_time (EggDateTime *edt);
241 static void normalize_date (EggDateTime *edt);
242 static void normalize_time (EggDateTime *edt);
243 static void parse_and_update_date (EggDateTime *edt);
244 static void parse_and_update_time (EggDateTime *edt);
245 static void update_date_label (EggDateTime *edt);
246 static void update_time_label (EggDateTime *edt);
249 static GtkHBoxClass *parent_class = NULL;
252 GtkType
253 egg_datetime_get_type (void)
255 static GtkType datetime_type = 0;
257 if (!datetime_type) {
258 static const GTypeInfo datetime_info = {
259 sizeof (EggDateTimeClass),
260 NULL, /* base_init */
261 NULL, /* base_finalize */
262 (GClassInitFunc) egg_datetime_class_init,
263 NULL, /* class_finalize */
264 NULL, /* class_data */
265 sizeof (EggDateTime),
266 0, /* n_preallocs */
267 (GInstanceInitFunc) egg_datetime_init
270 datetime_type = g_type_register_static (GTK_TYPE_HBOX, "EggDateTime", &datetime_info, 0);
273 return datetime_type;
276 static void
277 egg_datetime_class_init (EggDateTimeClass *klass)
279 GObjectClass *o_class;
280 GtkObjectClass *go_class;
281 GParamSpec *pspec;
283 parent_class = g_type_class_peek_parent (klass);
285 o_class = G_OBJECT_CLASS (klass);
286 go_class = GTK_OBJECT_CLASS (klass);
288 o_class->finalize = egg_datetime_finalize;
289 o_class->set_property = egg_datetime_set_property;
290 o_class->get_property = egg_datetime_get_property;
291 go_class->destroy = egg_datetime_destroy;
293 /* Properties */
295 pspec = g_param_spec_uint ("display-flags",
296 _("Display flags"),
297 _("Displayed date and/or time properties"),
298 0, G_MAXUINT, EGG_DATETIME_DISPLAY_DATE,
299 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
300 g_object_class_install_property (o_class, ARG_DISPLAY_MODE, pspec);
301 pspec = g_param_spec_boolean ("lazy",
302 _("Lazy mode"),
303 _("Lazy mode doesn't normalize entered date and time values"),
304 FALSE,
305 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
306 g_object_class_install_property (o_class, ARG_LAZY, pspec);
307 pspec = g_param_spec_uint ("year",
308 _("Year"),
309 _("Displayed year"),
310 1, 9999, 2000,
311 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
312 g_object_class_install_property (o_class, ARG_YEAR, pspec);
313 pspec = g_param_spec_uint ("month",
314 _("Month"),
315 _("Displayed month"),
316 G_DATE_JANUARY, G_DATE_DECEMBER, G_DATE_JANUARY,
317 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
318 g_object_class_install_property (o_class, ARG_MONTH, pspec);
319 pspec = g_param_spec_uint ("day",
320 _("Day"),
321 _("Displayed day of month"),
322 1, 31, 1,
323 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
324 g_object_class_install_property (o_class, ARG_DAY, pspec);
325 pspec = g_param_spec_uint ("hour",
326 _("Hour"),
327 _("Displayed hour"),
328 0, 23, 0,
329 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
330 g_object_class_install_property (o_class, ARG_HOUR, pspec);
331 pspec = g_param_spec_uint ("minute",
332 _("Minute"),
333 _("Displayed minute"),
334 0, 59, 0,
335 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
336 g_object_class_install_property (o_class, ARG_MINUTE, pspec);
337 /* second (time) not 2nd */
338 pspec = g_param_spec_uint ("second",
339 _("Second"),
340 _("Displayed second"),
341 0, 59, 0,
342 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
343 g_object_class_install_property (o_class, ARG_SECOND, pspec);
344 pspec = g_param_spec_uint ("clamp-minyear",
345 _("Lower limit year"),
346 _("Year part of the lower date limit"),
347 1, 9999, 1,
348 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
349 g_object_class_install_property (o_class, ARG_CLAMP_MINYEAR, pspec);
350 pspec = g_param_spec_uint ("clamp-maxyear",
351 _("Upper limit year"),
352 _("Year part of the upper date limit"),
353 1, 9999, 9999,
354 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
355 g_object_class_install_property (o_class, ARG_CLAMP_MAXYEAR, pspec);
356 pspec = g_param_spec_uint ("clamp-minmonth",
357 _("Lower limit month"),
358 _("Month part of the lower date limit"),
359 G_DATE_JANUARY, G_DATE_DECEMBER, G_DATE_JANUARY,
360 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
361 g_object_class_install_property (o_class, ARG_CLAMP_MINMONTH, pspec);
362 pspec = g_param_spec_uint ("clamp-maxmonth",
363 _("Upper limit month"),
364 _("Month part of the upper date limit"),
365 G_DATE_JANUARY, G_DATE_DECEMBER, G_DATE_DECEMBER,
366 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
367 g_object_class_install_property (o_class, ARG_CLAMP_MAXMONTH, pspec);
368 pspec = g_param_spec_uint ("clamp-minday",
369 _("Lower limit day"),
370 _("Day of month part of the lower date limit"),
371 1, 31, 1,
372 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
373 g_object_class_install_property (o_class, ARG_CLAMP_MINDAY, pspec);
374 pspec = g_param_spec_uint ("clamp-maxday",
375 _("Upper limit day"),
376 _("Day of month part of the upper date limit"),
377 1, 31, 31,
378 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
379 g_object_class_install_property (o_class, ARG_CLAMP_MAXDAY, pspec);
380 pspec = g_param_spec_uint ("clamp-minhour",
381 _("Lower limit hour"),
382 _("Hour part of the lower time limit"),
383 0, 23, 0,
384 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
385 g_object_class_install_property (o_class, ARG_CLAMP_MINHOUR, pspec);
386 pspec = g_param_spec_uint ("clamp-maxhour",
387 _("Upper limit hour"),
388 _("Hour part of the upper time limit"),
389 0, 23, 23,
390 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
391 g_object_class_install_property (o_class, ARG_CLAMP_MAXHOUR, pspec);
392 pspec = g_param_spec_uint ("clamp-minminute",
393 _("Lower limit minute"),
394 _("Minute part of the lower time limit"),
395 0, 59, 0,
396 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
397 g_object_class_install_property (o_class, ARG_CLAMP_MINMINUTE, pspec);
398 pspec = g_param_spec_uint ("clamp-maxminute",
399 _("Upper limit minute"),
400 _("Minute part of the upper time limit"),
401 0, 59, 59,
402 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
403 g_object_class_install_property (o_class, ARG_CLAMP_MAXMINUTE, pspec);
404 pspec = g_param_spec_uint ("clamp-minsecond",
405 _("Lower limit second"),
406 _("Second part of the lower time limit"),
407 0, 59, 0,
408 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
409 g_object_class_install_property (o_class, ARG_CLAMP_MINSECOND, pspec);
410 pspec = g_param_spec_uint ("clamp-maxsecond",
411 _("Upper limit second"),
412 _("Second part of the upper time limit"),
413 0, 59, 59,
414 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
415 g_object_class_install_property (o_class, ARG_CLAMP_MAXSECOND, pspec);
417 /* Signals */
419 egg_datetime_signals [SIGNAL_DATE_CHANGED] =
420 g_signal_new ("date-changed",
421 G_TYPE_FROM_CLASS (klass),
422 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
423 G_STRUCT_OFFSET (EggDateTimeClass, date_changed),
424 NULL, NULL,
425 g_cclosure_marshal_VOID__VOID,
426 G_TYPE_NONE, 0);
428 egg_datetime_signals [SIGNAL_TIME_CHANGED] =
429 g_signal_new ("time-changed",
430 G_TYPE_FROM_CLASS (klass),
431 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
432 G_STRUCT_OFFSET (EggDateTimeClass, time_changed),
433 NULL, NULL,
434 g_cclosure_marshal_VOID__VOID,
435 G_TYPE_NONE, 0);
438 static void
439 egg_datetime_init (EggDateTime *edt)
441 EggDateTimePrivate *priv;
442 GtkWidget *arrow, *hbox, *vbox, *frame;
443 GtkCalendarDisplayOptions cal_options;
444 priv = g_new0 (EggDateTimePrivate, 1);
445 edt->priv = priv;
447 /* Translators: Change this if you want to start weeks at mondays. */
448 priv->week_start_monday = strcmp (_("week-starts-monday: yes"), "week-starts-monday: yes") == 0 ? TRUE : FALSE;
450 priv->date_valid = FALSE;
451 priv->time_valid = FALSE;
453 /* Initialize Widgets */
455 gtk_box_set_spacing (GTK_BOX (edt), 4);
457 /* Date Widgets */
459 priv->date_box = gtk_hbox_new (FALSE, 0);
460 gtk_box_pack_start (GTK_BOX (edt), priv->date_box, TRUE, TRUE, 0);
462 priv->date_entry = gtk_entry_new ();
463 gtk_entry_set_width_chars (GTK_ENTRY (priv->date_entry), 13);
465 _add_atk_name_desc (priv->date_entry, _("Date"), _("Enter the date directly"));
467 g_signal_connect_swapped (G_OBJECT (priv->date_entry), "focus-out-event",
468 G_CALLBACK (date_focus_out), edt);
469 gtk_widget_show (priv->date_entry);
470 gtk_box_pack_start (GTK_BOX (priv->date_box), priv->date_entry, TRUE, TRUE, 0);
472 priv->date_button = gtk_toggle_button_new ();
474 _add_atk_name_desc (priv->date_button, _("Select Date"), _("Select the date from a calendar"));
476 gtk_box_pack_start (GTK_BOX (priv->date_box), priv->date_button, FALSE, FALSE, 0);
477 arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
478 gtk_container_add (GTK_CONTAINER (priv->date_button), arrow);
479 gtk_widget_show (arrow);
481 g_signal_connect_swapped (G_OBJECT (priv->date_button), "toggled",
482 G_CALLBACK (date_arrow_toggled), edt);
484 _add_atk_relation (priv->date_button, priv->date_entry,
485 ATK_RELATION_CONTROLLER_FOR, ATK_RELATION_CONTROLLED_BY);
487 /* Time Widgets */
489 priv->time_box = gtk_hbox_new (FALSE, 0);
490 gtk_box_pack_start (GTK_BOX (edt), priv->time_box, TRUE, TRUE, 0);
492 priv->time_entry = gtk_entry_new ();
493 gtk_entry_set_width_chars (GTK_ENTRY (priv->time_entry), 10);
495 _add_atk_name_desc (priv->time_entry, _("Time"), _("Enter the time directly"));
497 g_signal_connect_swapped (G_OBJECT (priv->time_entry), "focus-out-event",
498 G_CALLBACK (time_focus_out), edt);
499 gtk_widget_show (priv->time_entry);
500 gtk_box_pack_start (GTK_BOX (priv->time_box), priv->time_entry, TRUE, TRUE, 0);
502 priv->time_button = gtk_toggle_button_new ();
504 _add_atk_name_desc (priv->date_button, _("Select Time"), _("Select the time from a list"));
506 gtk_box_pack_start (GTK_BOX (priv->time_box), priv->time_button, FALSE, FALSE, 0);
507 arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
508 gtk_container_add (GTK_CONTAINER (priv->time_button), arrow);
509 gtk_widget_show (arrow);
510 g_signal_connect_swapped (G_OBJECT (priv->time_button), "toggled",
511 G_CALLBACK (time_arrow_toggled), edt);
513 _add_atk_relation (priv->time_button, priv->time_entry,
514 ATK_RELATION_CONTROLLER_FOR, ATK_RELATION_CONTROLLED_BY);
516 /* Calendar Popup */
518 priv->cal_popup = gtk_window_new (GTK_WINDOW_POPUP);
519 gtk_widget_set_events (priv->cal_popup,
520 gtk_widget_get_events (priv->cal_popup) | GDK_KEY_PRESS_MASK);
521 gtk_window_set_resizable (GTK_WINDOW (priv->cal_popup), FALSE);
522 g_signal_connect_swapped (G_OBJECT (priv->cal_popup), "delete-event",
523 G_CALLBACK (cal_popup_closed), edt);
524 g_signal_connect_swapped (G_OBJECT (priv->cal_popup), "key-press-event",
525 G_CALLBACK (cal_popup_key_pressed), edt);
526 g_signal_connect_swapped (G_OBJECT (priv->cal_popup), "button-press-event",
527 G_CALLBACK (cal_popup_button_pressed), edt);
529 vbox = gtk_vbox_new(FALSE, 0);
530 frame = gtk_frame_new(NULL);
531 priv->calendar = gtk_calendar_new ();
532 gtk_window_set_focus(GTK_WINDOW(priv->cal_popup), priv->calendar);
533 gtk_container_add (GTK_CONTAINER (frame), vbox);
534 gtk_container_add (GTK_CONTAINER (priv->cal_popup), frame);
535 cal_options = GTK_CALENDAR_SHOW_DAY_NAMES | GTK_CALENDAR_SHOW_HEADING;
536 if (priv->week_start_monday)
537 cal_options |= GTK_CALENDAR_WEEK_START_MONDAY;
538 gtk_calendar_display_options (GTK_CALENDAR (priv->calendar), cal_options);
539 gtk_box_pack_start(GTK_BOX(vbox), priv->calendar, TRUE, TRUE,0);
540 g_signal_connect_swapped (G_OBJECT (priv->calendar), "day-selected",
541 G_CALLBACK (cal_popup_changed), edt);
542 g_signal_connect_swapped (G_OBJECT (priv->calendar), "day-selected-double-click",
543 G_CALLBACK (cal_popup_double_click), edt);
544 hbox = gtk_hbox_new(FALSE, 0);
545 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE,0);
546 priv->today = gtk_button_new_with_mnemonic(_("_Today"));
547 priv->nodate = gtk_button_new_with_mnemonic(_("_No Date"));
548 gtk_box_pack_start(GTK_BOX(hbox), priv->today, TRUE, TRUE,0);
549 gtk_box_pack_start(GTK_BOX(hbox), priv->nodate, TRUE, TRUE,0);
550 g_signal_connect_swapped (G_OBJECT (priv->today), "clicked",
551 G_CALLBACK (cal_set_today), edt);
552 g_signal_connect_swapped (G_OBJECT (priv->nodate), "clicked",
553 G_CALLBACK (cal_set_nodate), edt);
556 gtk_widget_show_all(frame);
557 gtk_widget_show(priv->calendar);
558 /* Time Popup */
560 priv->time_popup = gtk_window_new (GTK_WINDOW_POPUP);
561 gtk_widget_set_events (priv->time_popup,
562 gtk_widget_get_events (priv->time_popup) | GDK_KEY_PRESS_MASK);
563 gtk_window_set_resizable (GTK_WINDOW (priv->time_popup), FALSE);
564 g_signal_connect_swapped (G_OBJECT (priv->time_popup), "delete-event",
565 G_CALLBACK (time_popup_closed), edt);
566 g_signal_connect_swapped (G_OBJECT (priv->time_popup), "key-press-event",
567 G_CALLBACK (time_popup_key_pressed), edt);
568 g_signal_connect_swapped (G_OBJECT (priv->time_popup), "button-press-event",
569 G_CALLBACK (time_popup_button_pressed), edt);
571 priv->timelist = timelist_new (edt);
573 gtk_window_set_default(GTK_WINDOW(priv->time_popup), gtk_bin_get_child(GTK_BIN(priv->timelist)));
575 timelist_set_selection_callback (TIMELIST (priv->timelist), G_CALLBACK (time_popup_changed), edt);
576 gtk_widget_set_size_request (priv->timelist, -1, 400);
577 gtk_container_add (GTK_CONTAINER (priv->time_popup), priv->timelist);
578 gtk_widget_show (priv->timelist);
579 if(priv->no_date) gtk_widget_set_sensitive(priv->time_box, FALSE);
582 static void
583 egg_datetime_set_property (GObject *object,
584 guint property_id,
585 const GValue *value,
586 GParamSpec *pspec)
588 EggDateTime *edt;
589 EggDateTimePrivate *priv;
591 g_return_if_fail (object != NULL);
592 g_return_if_fail (EGG_IS_DATETIME (object));
594 edt = EGG_DATETIME (object);
595 priv = edt->priv;
597 switch (property_id) {
598 case ARG_DISPLAY_MODE:
599 egg_datetime_set_display_mode (edt, g_value_get_uint (value));
600 break;
601 case ARG_LAZY:
602 egg_datetime_set_lazy (edt, g_value_get_boolean (value));
603 break;
604 case ARG_YEAR:
605 priv->year = g_value_get_uint (value);
606 break;
607 case ARG_MONTH:
608 priv->minute = g_value_get_uint (value);
609 break;
610 case ARG_DAY:
611 priv->day = g_value_get_uint (value);
612 break;
613 case ARG_HOUR:
614 priv->hour = g_value_get_uint (value);
615 break;
616 case ARG_MINUTE:
617 priv->minute = g_value_get_uint (value);
618 break;
619 case ARG_SECOND:
620 priv->second = g_value_get_uint (value);
621 break;
622 case ARG_CLAMP_MINYEAR:
623 priv->clamp_minyear = g_value_get_uint (value);
624 break;
625 case ARG_CLAMP_MINMONTH:
626 priv->clamp_minmonth = g_value_get_uint (value);
627 break;
628 case ARG_CLAMP_MINDAY:
629 priv->clamp_minday = g_value_get_uint (value);
630 break;
631 case ARG_CLAMP_MINHOUR:
632 priv->clamp_minhour = g_value_get_uint (value);
633 break;
634 case ARG_CLAMP_MINMINUTE:
635 priv->clamp_minminute = g_value_get_uint (value);
636 break;
637 case ARG_CLAMP_MINSECOND:
638 priv->clamp_minsecond = g_value_get_uint (value);
639 break;
640 case ARG_CLAMP_MAXYEAR:
641 priv->clamp_maxyear = g_value_get_uint (value);
642 break;
643 case ARG_CLAMP_MAXMONTH:
644 priv->clamp_maxmonth = g_value_get_uint (value);
645 break;
646 case ARG_CLAMP_MAXDAY:
647 priv->clamp_maxday = g_value_get_uint (value);
648 break;
649 case ARG_CLAMP_MAXHOUR:
650 priv->clamp_maxhour = g_value_get_uint (value);
651 break;
652 case ARG_CLAMP_MAXMINUTE:
653 priv->clamp_maxminute = g_value_get_uint (value);
654 break;
655 case ARG_CLAMP_MAXSECOND:
656 priv->clamp_maxsecond = g_value_get_uint (value);
657 break;
661 static void
662 egg_datetime_get_property (GObject *object,
663 guint property_id,
664 GValue *value,
665 GParamSpec *pspec)
667 EggDateTimePrivate *priv;
669 g_return_if_fail (object != NULL);
670 g_return_if_fail (EGG_IS_DATETIME (object));
672 priv = EGG_DATETIME (object)->priv;
674 switch (property_id) {
675 case ARG_DISPLAY_MODE:
676 g_value_set_uint (value, (guint) priv->display_mode);
677 break;
678 case ARG_LAZY:
679 g_value_set_boolean (value, priv->lazy);
680 break;
681 case ARG_YEAR:
682 g_value_set_uint (value, priv->year);
683 break;
684 case ARG_MONTH:
685 g_value_set_uint (value, priv->month);
686 break;
687 case ARG_DAY:
688 g_value_set_uint (value, priv->day);
689 break;
690 case ARG_HOUR:
691 g_value_set_uint (value, priv->hour);
692 break;
693 case ARG_MINUTE:
694 g_value_set_uint (value, priv->minute);
695 break;
696 case ARG_SECOND:
697 g_value_set_uint (value, priv->second);
698 break;
699 case ARG_CLAMP_MINYEAR:
700 g_value_set_uint (value, priv->clamp_minyear);
701 break;
702 case ARG_CLAMP_MINMONTH:
703 g_value_set_uint (value, priv->clamp_minmonth);
704 break;
705 case ARG_CLAMP_MINDAY:
706 g_value_set_uint (value, priv->clamp_minday);
707 break;
708 case ARG_CLAMP_MINHOUR:
709 g_value_set_uint (value, priv->clamp_minhour);
710 break;
711 case ARG_CLAMP_MINMINUTE:
712 g_value_set_uint (value, priv->clamp_minminute);
713 break;
714 case ARG_CLAMP_MINSECOND:
715 g_value_set_uint (value, priv->clamp_minsecond);
716 break;
717 case ARG_CLAMP_MAXYEAR:
718 g_value_set_uint (value, priv->clamp_maxyear);
719 break;
720 case ARG_CLAMP_MAXMONTH:
721 g_value_set_uint (value, priv->clamp_maxmonth);
722 break;
723 case ARG_CLAMP_MAXDAY:
724 g_value_set_uint (value, priv->clamp_maxday);
725 break;
726 case ARG_CLAMP_MAXHOUR:
727 g_value_set_uint (value, priv->clamp_maxhour);
728 break;
729 case ARG_CLAMP_MAXMINUTE:
730 g_value_set_uint (value, priv->clamp_maxminute);
731 break;
732 case ARG_CLAMP_MAXSECOND:
733 g_value_set_uint (value, priv->clamp_maxsecond);
734 break;
738 static void
739 egg_datetime_destroy (GtkObject *object)
741 EggDateTime *edt = EGG_DATETIME (object);
742 EggDateTimePrivate *priv = edt->priv;
744 if (priv->cal_popup) {
745 gtk_widget_destroy (priv->cal_popup);
746 priv->cal_popup = NULL;
749 if (priv->time_popup) {
750 gtk_widget_destroy (priv->time_popup);
751 priv->time_popup = NULL;
754 if (GTK_OBJECT_CLASS (parent_class)->destroy)
755 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
758 static void
759 egg_datetime_finalize (GObject *object)
761 EggDateTime *edt = EGG_DATETIME (object);
763 g_free (edt->priv);
765 if (G_OBJECT_CLASS (parent_class)->finalize)
766 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
770 * Utility Functions
773 /* Determine the number of bits, time_t uses. */
774 static guint
775 time_t_bits (void) {
776 guint i;
777 time_t t;
779 for (t = 1, i = 0; t != 0; t = t << 1, i++)
782 return i;
785 static gchar *
786 get_time_string (guint8 hour, guint8 minute, guint8 second)
788 gchar *s;
790 /* Translators: set this to anything else if you want to use a
791 * 24 hour clock.
793 if (!strcmp (_("24hr: no"), "24hr: yes")) {
794 const gchar *ampm_s;
796 if (hour < 12)
797 ampm_s = _("AM");
798 else
799 ampm_s = _("PM");
801 hour %= 12;
802 if (hour == 0)
803 hour = 12;
805 if (second <= 59)
806 /* Translators: This is hh:mm:ss AM/PM. */
807 s = g_strdup_printf (_("%02d:%02d:%02d %s"), hour, minute, second, ampm_s);
808 else
809 /* Translators: This is hh:mm AM/PM. */
810 s = g_strdup_printf (_("%02d:%02d %s"), hour, minute, ampm_s);
811 } else {
812 if (second <= 59)
813 /* Translators: This is hh:mm:ss. */
814 s = g_strdup_printf (_("%02d:%02d:%02d"), hour, minute, second);
815 else
816 /* Translators: This is hh:mm. */
817 s = g_strdup_printf (_("%02d:%02d"), hour, minute);
820 return s;
823 static void
824 popup_position (GtkWidget *widget, GtkWindow *popup)
826 GtkRequisition requisition;
827 gint x, y, width, height;
829 gtk_widget_size_request (GTK_WIDGET (popup), &requisition);
830 gdk_window_get_origin (widget->window, &x, &y);
832 x += widget->allocation.x;
833 y += widget->allocation.y;
834 width = widget->allocation.width;
835 height = widget->allocation.height;
837 x += width - requisition.width;
838 y += height;
840 if (x < 0)
841 x = 0;
842 if (y < 0)
843 y = 0;
845 gtk_window_move (popup, x, y);
848 static void
849 popup_show (GtkWindow *popup)
851 /* GdkCursor *cursor;*/
853 gtk_widget_show_all (GTK_WIDGET (popup));
854 gtk_widget_grab_focus (GTK_WIDGET (popup));
855 gtk_grab_add (GTK_WIDGET (popup));
857 /* Think its ugly to change the cursor.. */
859 cursor = gdk_cursor_new (GDK_ARROW);
860 gdk_pointer_grab (GTK_WIDGET (popup)->window, TRUE,
861 (GDK_BUTTON_PRESS_MASK
862 | GDK_BUTTON_RELEASE_MASK
863 | GDK_POINTER_MOTION_MASK),
864 NULL, cursor, GDK_CURRENT_TIME);
865 gdk_cursor_unref (cursor);
869 static void
870 popup_hide (GtkWindow *popup)
872 gtk_widget_hide (GTK_WIDGET (popup));
873 gtk_grab_remove (GTK_WIDGET (popup));
874 gdk_pointer_ungrab (GDK_CURRENT_TIME);
878 * Calendar Popup
881 static void
882 date_arrow_toggled (EggDateTime *edt, GtkToggleButton *button)
884 EggDateTimePrivate *priv = edt->priv;
886 if (!gtk_toggle_button_get_active (button))
887 return;
889 /* Set Date */
891 parse_date (edt);
893 if(priv->nodate == 0)
895 gtk_calendar_select_month (GTK_CALENDAR (priv->calendar), priv->month - 1, priv->year);
896 gtk_calendar_select_day (GTK_CALENDAR (priv->calendar), priv->day);
899 /* Position Popup */
901 popup_position (priv->date_button, GTK_WINDOW (priv->cal_popup));
903 /* Show Popup */
905 popup_show (GTK_WINDOW (priv->cal_popup));
908 static void
909 cal_popup_changed (EggDateTime *edt, GtkCalendar *calendar)
911 guint year, month, day;
912 gtk_calendar_get_date (GTK_CALENDAR (edt->priv->calendar), &year, &month, &day);
914 edt->priv->no_date = FALSE;
916 edt->priv->year = year;
917 edt->priv->month = month + 1;
918 edt->priv->day = day;
919 edt->priv->date_valid = TRUE;
921 normalize_date (edt);
922 update_date_label (edt);
924 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
927 static void
928 cal_set_today (EggDateTime *edt, GtkCalendar *calendar)
930 GDate *now;
931 guint year, month, day;
932 edt->priv->no_date = FALSE;
934 now = g_date_new();
935 g_date_set_time (now, time (NULL));
937 year = g_date_get_year(now);
938 month = g_date_get_month(now);
939 day = g_date_get_day(now);
941 edt->priv->year = year;
942 edt->priv->month = month;
943 edt->priv->day = day;
944 edt->priv->date_valid = TRUE;
946 gtk_calendar_select_month(GTK_CALENDAR (edt->priv->calendar), month-1, year);
947 gtk_calendar_select_day(GTK_CALENDAR (edt->priv->calendar), day);
948 gtk_widget_show_all(edt->priv->calendar);
950 gtk_calendar_set_date (GTK_CALENDAR (edt->priv->calendar), &year, &month, &day);
952 normalize_date (edt);
953 update_date_label (edt);
955 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
956 cal_popup_hide(edt);
957 g_date_free(now);
961 static void
962 cal_set_nodate (EggDateTime *edt, GtkCalendar *calendar)
964 edt->priv->no_date = TRUE;
966 normalize_date (edt);
967 update_date_label (edt);
969 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
970 cal_popup_hide(edt);
974 void egg_set_nodate(EggDateTime *edt, gboolean val)
976 edt->priv->no_date = val;
978 normalize_date (edt);
979 update_date_label (edt);
981 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
985 gboolean egg_get_nodate(EggDateTime *edt)
987 return edt->priv->no_date;
991 static void
992 cal_popup_double_click (EggDateTime *edt, GtkCalendar *calendar)
994 edt->priv->no_date = FALSE;
996 gtk_widget_set_sensitive(priv->time_box, edt->priv->no_date);
998 cal_popup_hide (edt);
1001 static gboolean
1002 cal_popup_key_pressed (EggDateTime *edt, GdkEventKey *event, GtkWidget *widget)
1004 if (event->keyval != GDK_Escape)
1005 return FALSE;
1007 g_signal_stop_emission_by_name (G_OBJECT (widget), "key_press_event");
1009 cal_popup_hide (edt);
1011 return TRUE;
1014 static gboolean
1015 cal_popup_button_pressed (EggDateTime *edt, GdkEventButton *event, GtkWidget *widget)
1017 GtkWidget *child;
1019 child = gtk_get_event_widget ((GdkEvent *) event);
1021 /* We don't ask for button press events on the grab widget, so
1022 * if an event is reported directly to the grab widget, it must
1023 * be on a window outside the application (and thus we remove
1024 * the popup window). Otherwise, we check if the widget is a child
1025 * of the grab widget, and only remove the popup window if it
1026 * is not.
1028 if (child != widget) {
1029 while (child) {
1030 if (child == widget)
1031 return FALSE;
1032 child = child->parent;
1036 cal_popup_hide (edt);
1038 return TRUE;
1041 static gboolean
1042 cal_popup_closed (EggDateTime *edt, GtkWindow *popup)
1044 cal_popup_hide (edt);
1046 return TRUE;
1049 static void
1050 cal_popup_hide (EggDateTime *edt)
1052 EggDateTimePrivate *priv = edt->priv;
1054 popup_hide (GTK_WINDOW (priv->cal_popup));
1056 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->date_button), FALSE);
1057 gtk_widget_grab_focus (priv->date_entry);
1059 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
1063 * Time Popup
1066 static void
1067 time_arrow_toggled (EggDateTime *edt, GtkToggleButton *button)
1069 EggDateTimePrivate *priv = edt->priv;
1070 gint hour, minute;
1071 if (!gtk_toggle_button_get_active (button))
1072 return;
1074 /* Set Time */
1075 parse_time (edt);
1077 /* Position Popup */
1079 popup_position (priv->time_button, GTK_WINDOW (priv->time_popup));
1081 /* Show Popup */
1082 /* dirty hack to stop it selecting the first item when showing for the first time in new window*/
1083 /* I should fix this.. because this is quiet nasty */
1084 hour = priv->hour;
1085 minute= priv->minute;
1086 popup_show (GTK_WINDOW (priv->time_popup));
1087 priv->hour = hour;
1088 priv->minute = minute;
1089 timelist_set_time (TIMELIST (priv->timelist), priv->hour, priv->minute);
1090 update_time_label(edt);
1093 static void
1094 time_popup_changed (EggDateTime *edt, Timelist *timelist)
1096 EggDateTimePrivate *priv = edt->priv;
1098 if (!timelist_get_time (timelist, &priv->hour, &priv->minute))
1099 return;
1101 priv->second = 0;
1102 priv->time_valid = TRUE;
1104 normalize_time (edt);
1106 if(GTK_WIDGET_VISIBLE(timelist))update_time_label (edt);
1108 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_TIME_CHANGED], 0);
1111 static gboolean
1112 time_popup_key_pressed (EggDateTime *edt, GdkEventKey *event, GtkWidget *widget)
1114 if (event->keyval != GDK_Escape)
1115 return FALSE;
1117 g_signal_stop_emission_by_name (G_OBJECT (widget), "key_press_event");
1119 time_popup_hide (edt);
1121 return TRUE;
1124 static gboolean
1125 time_popup_button_pressed (EggDateTime *edt, GdkEventButton *event, GtkWidget *widget)
1127 GtkWidget *child;
1129 child = gtk_get_event_widget ((GdkEvent *) event);
1131 /* We don't ask for button press events on the grab widget, so
1132 * if an event is reported directly to the grab widget, it must
1133 * be on a window outside the application (and thus we remove
1134 * the popup window). Otherwise, we check if the widget is a child
1135 * of the grab widget, and only remove the popup window if it
1136 * is not.
1138 if (child != widget) {
1139 while (child) {
1140 if (child == widget)
1141 return FALSE;
1142 child = child->parent;
1146 time_popup_hide (edt);
1148 return TRUE;
1151 static gboolean
1152 time_popup_closed (EggDateTime *edt, GtkWindow *popup)
1154 time_popup_hide (edt);
1156 return TRUE;
1159 static void
1160 time_popup_hide (EggDateTime *edt)
1162 EggDateTimePrivate *priv = edt->priv;
1163 popup_hide (GTK_WINDOW (priv->time_popup));
1165 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->time_button), FALSE);
1166 gtk_widget_grab_focus (priv->time_entry);
1168 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_TIME_CHANGED], 0);
1172 * Callbacks
1175 static gboolean
1176 date_focus_out (EggDateTime *edt, GtkEntry *entry)
1178 parse_date (edt);
1179 update_date_label (edt);
1181 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
1183 return FALSE;
1186 static gboolean
1187 time_focus_out (EggDateTime *edt, GtkEntry *entry)
1189 parse_time (edt);
1190 update_time_label (edt);
1192 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_TIME_CHANGED], 0);
1194 return FALSE;
1198 * Private Methods
1201 static void
1202 apply_display_mode (EggDateTime *edt)
1204 if (edt->priv->display_mode & EGG_DATETIME_DISPLAY_DATE)
1205 gtk_widget_show (edt->priv->date_box);
1206 else
1207 gtk_widget_hide (edt->priv->date_box);
1208 if (edt->priv->display_mode & EGG_DATETIME_DISPLAY_MONTH)
1209 gtk_widget_show (edt->priv->date_button);
1210 else
1211 gtk_widget_hide (edt->priv->date_button);
1213 if (edt->priv->display_mode & EGG_DATETIME_DISPLAY_TIME)
1214 gtk_widget_show (edt->priv->time_box);
1215 else
1216 gtk_widget_hide (edt->priv->time_box);
1217 if (edt->priv->display_mode & EGG_DATETIME_DISPLAY_HOUR)
1218 gtk_widget_show (edt->priv->time_button);
1219 else
1220 gtk_widget_hide (edt->priv->time_button);
1223 static void
1224 clamp_time_labels (EggDateTime *edt)
1226 EggDateTimePrivate *priv = edt->priv;
1228 timelist_clamp (TIMELIST (priv->timelist),
1229 priv->clamp_minhour,
1230 priv->clamp_minminute,
1231 priv->clamp_maxhour,
1232 priv->clamp_maxminute);
1235 /* Updates the date entry to the current date. */
1236 static void
1237 update_date_label (EggDateTime *edt)
1239 EggDateTimePrivate *priv = edt->priv;
1240 gchar *s;
1241 gtk_widget_set_sensitive(priv->time_box, !priv->no_date);
1242 if(priv->no_date)
1244 gtk_entry_set_text(GTK_ENTRY(priv->date_entry), _("No Date"));
1245 return;
1248 if (!priv->date_valid) {
1249 gtk_entry_set_text (GTK_ENTRY (priv->date_entry), "");
1250 return;
1253 /* TODO: should handle other display modes as well... */
1255 /* Translators: This is yyyy-mm-dd. */
1256 s = g_strdup_printf (_("%04d-%02d-%02d"), priv->year, priv->month, priv->day);
1257 gtk_entry_set_text (GTK_ENTRY (priv->date_entry), s);
1258 g_free (s);
1261 /* Updates the time entry to the current time. */
1262 static void
1263 update_time_label (EggDateTime *edt)
1265 EggDateTimePrivate *priv = edt->priv;
1266 gchar *s;
1268 if (!priv->time_valid) {
1269 gtk_entry_set_text (GTK_ENTRY (priv->time_entry), "");
1270 return;
1273 /* TODO: should handle other display modes as well... */
1275 if ((priv->display_mode & EGG_DATETIME_DISPLAY_SECOND) ||
1276 (priv->display_mode & EGG_DATETIME_DISPLAY_SECOND_OPT))
1277 s = get_time_string (priv->hour, priv->minute, priv->second);
1278 else {
1279 if(priv->hour == -1 && priv->minute == 0)
1280 s = g_strdup(_("no end time"));
1281 else s = get_time_string (priv->hour, priv->minute, 0xff);
1283 gtk_entry_set_text (GTK_ENTRY (priv->time_entry), s);
1285 g_free (s);
1288 /* Parse the current date entry and normalize the date. */
1289 static void
1290 parse_date (EggDateTime *edt)
1292 GDate date;
1293 g_date_set_parse (&date, gtk_entry_get_text (GTK_ENTRY (edt->priv->date_entry)));
1294 if (!g_date_valid (&date)) {
1295 edt->priv->no_date = TRUE;
1296 gtk_entry_set_text(GTK_ENTRY(edt->priv->date_entry), _("No Date"));
1297 return;
1300 edt->priv->year = date.year;
1301 edt->priv->month = date.month;
1302 edt->priv->day = date.day;
1303 edt->priv->date_valid = TRUE;
1305 normalize_date (edt);
1308 /* Parse the current time entry and normalize the time. */
1309 static void
1310 parse_time (EggDateTime *edt)
1312 const gchar *s;
1313 gchar *scp;
1314 unsigned int hour, minute, second = 0;
1315 size_t len1, len2, len3;
1317 /* Retrieve and Parse String */
1319 s = gtk_entry_get_text (GTK_ENTRY (edt->priv->time_entry));
1321 /* Translators: This is hh:mm:ss AM/PM. */
1322 if (sscanf (s, _("%u:%u:%u"), &hour, &minute, &second) < 2) {
1323 if (sscanf (s, "%u:%u:%u", &hour, &minute, &second) < 2) {
1324 if (edt->priv->lazy)
1325 edt->priv->time_valid = FALSE;
1326 return;
1330 if (hour > 23 || minute > 59 || second > 59) {
1331 if (edt->priv->lazy)
1332 edt->priv->time_valid = FALSE;
1333 return;
1336 /* AM/PM Handling */
1338 scp = g_strdup (s);
1339 scp = g_strchomp (scp);
1341 len1 = strlen (_("AM"));
1342 len2 = strlen (_("PM"));
1343 len3 = strlen (scp);
1345 if (len1 < len3 && !strcasecmp (scp + len3 - len1, _("AM"))) {
1346 if (hour == 12)
1347 hour = 0;
1349 if (len2 < len3 && !strcasecmp (scp + len3 - len2, _("PM"))) {
1350 if (hour == 12)
1351 hour = 0;
1352 hour += 12;
1355 /* Store Time */
1357 edt->priv->hour = hour;
1358 edt->priv->minute = minute;
1359 edt->priv->second = second;
1360 edt->priv->time_valid = TRUE;
1362 /* Cleanup */
1364 g_free (scp);
1366 normalize_time (edt);
1369 /* Clamp the current date to the date clamp range if lazy is turned off. */
1370 static void
1371 normalize_date (EggDateTime *edt)
1373 GDate date, min_date, max_date;
1375 if (edt->priv->lazy)
1376 return;
1378 g_date_clear (&date, 1);
1379 g_date_set_dmy (&date, edt->priv->day, edt->priv->month, edt->priv->year);
1380 g_date_clear (&min_date, 1);
1381 g_date_set_dmy (&min_date, edt->priv->clamp_minday, edt->priv->clamp_minmonth, edt->priv->clamp_minyear);
1382 g_date_clear (&max_date, 1);
1383 g_date_set_dmy (&max_date, edt->priv->clamp_maxday, edt->priv->clamp_maxmonth, edt->priv->clamp_maxyear);
1385 g_date_clamp (&date, &min_date, &max_date);
1387 edt->priv->year = date.year;
1388 edt->priv->month = date.month;
1389 edt->priv->day = date.day;
1390 edt->priv->date_valid = TRUE;
1393 /* Clamp the current time to the time clamp range if lazy is turned off. */
1394 static void
1395 normalize_time (EggDateTime *edt)
1397 if (edt->priv->lazy)
1398 return;
1400 if (edt->priv->hour < edt->priv->clamp_minhour) {
1401 edt->priv->hour = -1;;
1402 edt->priv->minute = edt->priv->clamp_minminute;
1403 edt->priv->second = edt->priv->clamp_minsecond;
1404 } else if (edt->priv->hour == edt->priv->clamp_minhour) {
1405 if (edt->priv->minute < edt->priv->clamp_minminute) {
1406 edt->priv->minute = edt->priv->clamp_minminute;
1407 edt->priv->second = edt->priv->clamp_minsecond;
1408 } else if (edt->priv->minute == edt->priv->clamp_minminute) {
1409 if (edt->priv->second < edt->priv->clamp_minsecond)
1410 edt->priv->second = edt->priv->clamp_minsecond;
1414 if (edt->priv->hour > edt->priv->clamp_maxhour) {
1415 edt->priv->hour = edt->priv->clamp_maxhour;
1416 edt->priv->minute = edt->priv->clamp_maxminute;
1417 edt->priv->second = edt->priv->clamp_maxsecond;
1418 } else if (edt->priv->hour == edt->priv->clamp_maxhour) {
1419 if (edt->priv->minute > edt->priv->clamp_maxminute) {
1420 edt->priv->minute = edt->priv->clamp_maxminute;
1421 edt->priv->second = edt->priv->clamp_maxsecond;
1422 } else if (edt->priv->minute == edt->priv->clamp_maxminute) {
1423 if (edt->priv->second > edt->priv->clamp_maxsecond)
1424 edt->priv->second = edt->priv->clamp_maxsecond;
1428 edt->priv->time_valid = TRUE;
1431 static void
1432 parse_and_update_date (EggDateTime *edt)
1434 if (edt->priv->lazy)
1435 return;
1437 parse_date (edt);
1438 update_date_label (edt);
1441 static void
1442 parse_and_update_time (EggDateTime *edt)
1444 if (edt->priv->lazy)
1445 return;
1447 parse_time (edt);
1448 update_time_label (edt);
1452 * Public Methods
1456 * egg_datetime_new:
1458 * Creates a new #EggDateTime widget. By default this widget will show
1459 * only the date component and is set to the current date and time.
1461 * Return value: a newly created #EggDateTime widget
1463 GtkWidget *
1464 egg_datetime_new (void)
1466 EggDateTime *edt;
1468 edt = g_object_new (EGG_TYPE_DATETIME, NULL);
1469 egg_datetime_set_from_time_t (edt, time (NULL));
1471 return GTK_WIDGET (edt);
1475 * egg_datetime_new_from_time_t:
1476 * @t: initial time and date
1478 * Creates a new #EggDateTime widget and sets it to the date and time
1479 * given as @t argument. This does also call egg_datetime_set_clamp_time_t().
1480 * By default this widget will show only the date component.
1482 * Return value: a newly created #EggDateTime widget
1484 GtkWidget *
1485 egg_datetime_new_from_time_t (time_t t)
1487 EggDateTime *edt;
1489 g_return_val_if_fail (t >= 0, NULL);
1491 edt = g_object_new (EGG_TYPE_DATETIME, NULL);
1492 egg_datetime_set_from_time_t (edt, t);
1493 egg_datetime_set_clamp_time_t (edt);
1495 return GTK_WIDGET (edt);
1499 * egg_datetime_new_from_struct_tm:
1500 * @tm: initial time and date
1502 * Creates a new #EggDateTime widget and sets it to the date and time
1503 * given as @tm argument. By default this widget will show only the date
1504 * component.
1506 * Return value: a newly created #EggDateTime widget
1508 GtkWidget *
1509 egg_datetime_new_from_struct_tm (struct tm *tm)
1511 EggDateTime *edt;
1513 g_return_val_if_fail (tm != NULL, NULL);
1515 edt = g_object_new (EGG_TYPE_DATETIME, NULL);
1516 egg_datetime_set_from_struct_tm (edt, tm);
1518 return GTK_WIDGET (edt);
1522 * egg_datetime_new_from_gdate:
1523 * @date: initial time and date
1525 * Creates a new #EggDateTime widget and sets it to the date and time
1526 * given as @date argument. By default this widget will show only the
1527 * date component.
1529 * Return value: a newly created #EggDateTime widget
1531 GtkWidget *
1532 egg_datetime_new_from_gdate (GDate *date)
1534 EggDateTime *edt;
1536 g_return_val_if_fail (date != NULL, NULL);
1538 edt = g_object_new (EGG_TYPE_DATETIME, NULL);
1539 egg_datetime_set_from_gdate (edt, date);
1541 return GTK_WIDGET (edt);
1545 * egg_datetime_new_from_datetime:
1546 * @year: initial year
1547 * @month: initial month
1548 * @day: initial day
1549 * @hour: initial hour
1550 * @minute: initial minute
1551 * @second: initial second
1553 * Creates a new #EggDateTime widget and sets it to the date and time
1554 * given as arguments.
1556 * Return value: a newly created #EggDateTime widget
1558 GtkWidget *
1559 egg_datetime_new_from_datetime (GDateYear year, GDateMonth month, GDateDay day, guint8 hour, guint8 minute, guint8 second)
1561 EggDateTime *edt;
1563 edt = g_object_new (EGG_TYPE_DATETIME, NULL);
1564 egg_datetime_set_date (edt, year, month, day);
1565 egg_datetime_set_time (edt, hour, minute, second);
1567 return GTK_WIDGET (edt);
1571 * egg_datetime_set_none:
1572 * @edt: an #EggDateTime
1574 * Sets the date to an invalid value. If lazy mode is turned off the date
1575 * and time will be set to a random value.
1577 void
1578 egg_datetime_set_none (EggDateTime *edt)
1580 g_return_if_fail (edt != NULL);
1581 g_return_if_fail (EGG_IS_DATETIME (edt));
1583 edt->priv->date_valid = FALSE;
1584 edt->priv->time_valid = FALSE;
1586 update_date_label (edt);
1587 update_time_label (edt);
1589 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
1590 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_TIME_CHANGED], 0);
1594 * egg_datetime_set_from_time_t:
1595 * @edt: an #EggDateTime
1596 * @t: date and time to set the widget to
1598 * Sets the date and time of the widget to @t.
1600 void
1601 egg_datetime_set_from_time_t (EggDateTime *edt, time_t t)
1603 struct tm tm;
1605 g_return_if_fail (edt != NULL);
1606 g_return_if_fail (EGG_IS_DATETIME (edt));
1608 if (localtime_r (&t, &tm)) {
1609 egg_datetime_set_date (edt, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
1610 egg_datetime_set_time (edt, tm.tm_hour, tm.tm_min, tm.tm_sec);
1611 } else
1612 egg_datetime_set_none (edt);
1616 * egg_datetime_get_as_time_t:
1617 * @edt: an #EggDateTime
1618 * @t: pointer to a %time_t value
1620 * Returns the current time as a %time_t value. If the currently entered
1621 * value is invalid and lazy mode is turned on or if the entered date
1622 * can't be represented as a %time_t value, the value is set to -1 and
1623 * FALSE is returned.
1625 * Return value: success indicator
1627 gboolean
1628 egg_datetime_get_as_time_t (EggDateTime *edt, time_t *t)
1630 struct tm tm;
1631 GDateYear year;
1632 GDateMonth month;
1633 GDateDay day;
1634 gint hour, minute, second;
1636 g_return_val_if_fail (edt != NULL, FALSE);
1637 g_return_val_if_fail (EGG_IS_DATETIME (edt), FALSE);
1639 if (!t)
1640 return FALSE;
1642 if (!egg_datetime_get_date (edt, &year, &month, &day)) {
1643 *t = -1;
1644 return FALSE;
1646 if (!egg_datetime_get_time (edt, &hour, &minute, &second)) {
1647 *t = -1;
1648 return FALSE;
1651 memset (&tm, 0, sizeof (struct tm));
1652 tm.tm_year = year - 1900;
1653 tm.tm_mon = month - 1;
1654 tm.tm_mday = day;
1655 tm.tm_hour = hour;
1656 tm.tm_min = minute;
1657 tm.tm_sec = second;
1659 *t = mktime (&tm);
1661 if (*t < 0) {
1662 *t = -1;
1663 return FALSE;
1666 return TRUE;
1670 * egg_datetime_set_from_struct_tm:
1671 * @edt: an #EggDateTime
1672 * @tm: date and time to set the widget to
1674 * Sets the date and time of the widget to @tm.
1676 void
1677 egg_datetime_set_from_struct_tm (EggDateTime *edt, struct tm *tm)
1679 g_return_if_fail (edt != NULL);
1680 g_return_if_fail (EGG_IS_DATETIME (edt));
1681 g_return_if_fail (tm != NULL);
1683 egg_datetime_set_date (edt, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
1684 egg_datetime_set_time (edt, tm->tm_hour, tm->tm_min, tm->tm_sec);
1688 * egg_datetime_get_as_struct_tm:
1689 * @edt: an #EggDateTime
1690 * @tm: pointer to an allocated struct tm
1692 * Fill the supplied struct tm with the widget's current date and time.
1693 * If the currently entered value is invalid and lazy mode is turned
1694 * on or if the entered date can't be represented as a struct tm, the
1695 * struct is filled with invalid data and FALSE is returned.
1697 * Return value: success indicator
1699 gboolean
1700 egg_datetime_get_as_struct_tm (EggDateTime *edt, struct tm *tm)
1702 GDateYear year;
1703 GDateMonth month;
1704 GDateDay day;
1705 gint hour, minute, second;
1707 g_return_val_if_fail (edt != NULL, FALSE);
1708 g_return_val_if_fail (EGG_IS_DATETIME (edt), FALSE);
1710 if (!tm)
1711 return FALSE;
1713 memset (tm, 0, sizeof (struct tm));
1715 if (!egg_datetime_get_date (edt, &year, &month, &day))
1716 return FALSE;
1717 if (!egg_datetime_get_time (edt, &hour, &minute, &second))
1718 return FALSE;
1720 tm->tm_year = year - 1900;
1721 tm->tm_mon = month - 1;
1722 tm->tm_mday = day;
1723 tm->tm_hour = hour;
1724 tm->tm_min = minute;
1725 tm->tm_sec = second;
1727 mktime (tm);
1729 return TRUE;
1733 * egg_datetime_set_from_gdate:
1734 * @edt: an #EggDateTime
1735 * @date: date to set the widget to
1737 * Sets the date of the widget to @date. The time will remain unchanged.
1739 void
1740 egg_datetime_set_from_gdate (EggDateTime *edt, GDate *date)
1742 GDateYear year;
1743 GDateMonth month;
1744 GDateDay day;
1746 g_return_if_fail (edt != NULL);
1747 g_return_if_fail (EGG_IS_DATETIME (edt));
1748 g_return_if_fail (date != NULL);
1750 year = g_date_get_year(date);
1751 month = g_date_get_month(date);
1752 day = g_date_get_day(date);
1753 g_return_if_fail(g_date_valid_dmy(day, month, year));
1755 if (g_date_valid (date))
1756 egg_datetime_set_date (edt, year, month, day);
1757 else
1758 egg_datetime_set_none (edt);
1762 * egg_datetime_get_as_gdate:
1763 * @edt: an #EggDateTime
1764 * @date: pointer to an allocated #GDate
1766 * Fills the supplied #GDate with the widget's current date. If the
1767 * currently entered date value is invalid and lazy mode is turned
1768 * on or if the entered date can't be represented as a #GDate, the
1769 * @date is set to an invalid value and FALSE is returned.
1771 * Return value: success indicator
1773 gboolean
1774 egg_datetime_get_as_gdate (EggDateTime *edt, GDate *date)
1776 GDateYear year;
1777 GDateMonth month;
1778 GDateDay day;
1780 g_return_val_if_fail (edt != NULL, FALSE);
1781 g_return_val_if_fail (EGG_IS_DATETIME (edt), FALSE);
1783 if (!date)
1784 return FALSE;
1786 g_date_clear (date, 1);
1788 if (!egg_datetime_get_date (edt, &year, &month, &day))
1789 return FALSE;
1791 g_date_set_dmy (date, day, month, year);
1793 return TRUE;
1797 * egg_datetime_set_date:
1798 * @edt: an #EggDateTime
1799 * @year: a #guint16 between 1 and 9999
1800 * @month: a #guint8 between 1 and 12
1801 * @day: a #guint8 between 1 and 28-31 (depending on @month)
1803 * Sets the date of the widget. The time will remain unchanged.
1805 void
1806 egg_datetime_set_date (EggDateTime *edt, GDateYear year, GDateMonth month, GDateDay day)
1808 g_return_if_fail (edt != NULL);
1809 g_return_if_fail (EGG_IS_DATETIME (edt));
1810 g_return_if_fail (year >= 1 && year <= 9999);
1811 g_return_if_fail (month >= 1 && month <= 12);
1812 g_return_if_fail (day >= 1 && day <= g_date_get_days_in_month (month, year));
1814 edt->priv->year = year;
1815 edt->priv->month = month;
1816 edt->priv->day = day;
1817 edt->priv->date_valid = TRUE;
1818 gtk_calendar_select_month(GTK_CALENDAR(edt->priv->calendar), month-1, year);
1819 gtk_calendar_select_day(GTK_CALENDAR(edt->priv->calendar), day);
1821 normalize_date (edt);
1822 update_date_label (edt);
1824 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
1828 * egg_datetime_get_date:
1829 * @edt: an #EggDateTime
1830 * @year: a pointer to a #guint16 or %NULL
1831 * @month: a pointer to #guint8 or %NULL
1832 * @day: a pointer to a #guint8 or %NULL
1834 * Fills the supplied arguments with the widget's current date. If the
1835 * currently entered date value is invalid and lazy mode is turned
1836 * on, the arguments are set to %EGG_DATETIME_INVALID_DATE and FALSE
1837 * is returned.
1839 * Return value: success indicator
1841 gboolean
1842 egg_datetime_get_date (EggDateTime *edt, GDateYear *year, GDateMonth *month, GDateDay *day)
1844 g_return_val_if_fail (edt != NULL, FALSE);
1845 g_return_val_if_fail (EGG_IS_DATETIME (edt), FALSE);
1847 parse_date (edt);
1849 if (!edt->priv->date_valid) {
1850 if (year)
1851 *year = 0;
1852 if (month)
1853 *month = 0;
1854 if (day)
1855 *day = 0;
1856 return FALSE;
1859 if (year)
1860 *year = edt->priv->year;
1861 if (month)
1862 *month = edt->priv->month;
1863 if (day)
1864 *day = edt->priv->day;
1866 return TRUE;
1870 * egg_datetime_set_time:
1871 * @edt: an #EggDateTime
1872 * @hour: a #guint8 between 0 and 23
1873 * @minute: a #guint8 between 0 and 59
1874 * @second: a #guint8 between 0 and 59
1876 * Sets the time of the widget. The date will remain unchanged.
1878 void
1879 egg_datetime_set_time (EggDateTime *edt, gint hour, gint minute, guint8 second)
1881 g_return_if_fail (edt != NULL);
1882 g_return_if_fail (EGG_IS_DATETIME (edt));
1883 g_return_if_fail (hour <= 23);
1884 g_return_if_fail (minute <= 59);
1885 g_return_if_fail (second <= 59);
1887 edt->priv->hour = hour;
1888 edt->priv->minute = minute;
1889 edt->priv->second = second;
1890 edt->priv->time_valid = TRUE;
1892 normalize_time (edt);
1893 update_time_label (edt);
1895 timelist_set_time(edt->priv->timelist, hour, minute);
1897 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_TIME_CHANGED], 0);
1901 * egg_datetime_get_date:
1902 * @edt: an #EggDateTime
1903 * @hour: a pointer to a #guint8 or %NULL
1904 * @minute: a pointer to #guint8 or %NULL
1905 * @second: a pointer to a #guint8 or %NULL
1907 * Fills the supplied arguments with the widget's current time. If the
1908 * currently entered time value is invalid and lazy mode is turned
1909 * on, the arguments are set to %EGG_DATETIME_INVALID_TIME and FALSE
1910 * is returned.
1912 * Return value: success indicator
1914 gboolean
1915 egg_datetime_get_time (EggDateTime *edt, gint *hour, gint *minute, gint *second)
1917 g_return_val_if_fail (edt != NULL, FALSE);
1918 g_return_val_if_fail (EGG_IS_DATETIME (edt), FALSE);
1920 parse_time (edt);
1922 if (!edt->priv->time_valid) {
1923 if (hour)
1924 *hour = 0xff;
1925 if (minute)
1926 *minute = 0xff;
1927 if (second)
1928 *second = 0xff;
1929 return FALSE;
1932 if (hour)
1933 *hour = edt->priv->hour;
1934 if (minute)
1935 *minute = edt->priv->minute;
1936 if (second)
1937 *second = edt->priv->second;
1939 return TRUE;
1943 * egg_datetime_set_lazy:
1944 * @edt: an #EggDateTime
1945 * @lazy: a boolean value
1947 * Turns the widget's lazy mode on or off. In lazy mode the widget will
1948 * allow invalid values to be entered. If lazy mode is turned off the
1949 * widget will normalize all invalid values entered in the date and time
1950 * widgets to the nearest valid value. This guarantees that the get methods
1951 * will always return valid values.
1953 * Lazy mode defaults to %TRUE.
1955 void
1956 egg_datetime_set_lazy (EggDateTime *edt, gboolean lazy)
1958 g_return_if_fail (edt != NULL);
1959 g_return_if_fail (EGG_IS_DATETIME (edt));
1961 edt->priv->lazy = lazy ? TRUE : FALSE;
1963 parse_and_update_date (edt);
1964 parse_and_update_time (edt);
1966 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
1967 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_TIME_CHANGED], 0);
1971 * egg_datetime_get_lazy:
1972 * @edt: an #EggDateTime
1974 * Returns whether the widget is in lazy mode.
1976 * Return value: a boolean value
1978 gboolean
1979 egg_datetime_get_lazy (EggDateTime *edt)
1981 g_return_val_if_fail (edt != NULL, FALSE);
1982 g_return_val_if_fail (EGG_IS_DATETIME (edt), FALSE);
1984 return edt->priv->lazy;
1988 * egg_datetime_set_display_mode:
1989 * @edt: an #EggDateTime
1990 * @mode: new display mode
1992 * Sets the widget's new display mode to @mode. The display mode defaults
1993 * to %EGG_DATETIME_DISPLAY_DATE.
1995 void
1996 egg_datetime_set_display_mode (EggDateTime *edt, EggDateTimeDisplayMode mode)
1998 g_return_if_fail (edt != NULL);
1999 g_return_if_fail (EGG_IS_DATETIME (edt));
2001 edt->priv->display_mode = mode;
2003 apply_display_mode (edt);
2007 * egg_datetime_get_display_mode:
2008 * @edt: an #EggDateTime
2010 * Returns the current display mode.
2012 * Return value: The current display mode.
2014 EggDateTimeDisplayMode
2015 egg_datetime_get_display_mode (EggDateTime *edt)
2017 g_return_val_if_fail (edt != NULL, 0);
2018 g_return_val_if_fail (EGG_IS_DATETIME (edt), 0);
2020 return edt->priv->display_mode;
2024 * egg_datetime_set_clamp_date:
2025 * @edt: an #EggDateTime
2026 * @minyear: minimum year
2027 * @minmonth: minimum month
2028 * @minday: minimum day
2029 * @maxyear: maximum year
2030 * @maxmonth: maximum month
2031 * @maxday: maximum day
2033 * Limits the allowed dates to the range given. If lazy mode is
2034 * turned off, dates that are outside of this range are snapped to the
2035 * minimum or maximum date. Otherwise such dates return an invalid value.
2037 * This defaults to the minimum date 1-1-1 and maximum date 9999-12-31.
2038 * The maximum year is always limited to 9999.
2040 void
2041 egg_datetime_set_clamp_date (EggDateTime *edt,
2042 GDateYear minyear,
2043 GDateMonth minmonth,
2044 GDateDay minday,
2045 GDateYear maxyear,
2046 GDateMonth maxmonth,
2047 GDateDay maxday)
2049 if (maxyear > 9999)
2050 maxyear = 9999;
2052 g_return_if_fail (minyear >= 1 && minyear <= 9999 && maxyear >= 1);
2053 g_return_if_fail (minmonth >= 1 && minmonth <= 12 && maxmonth >= 1 && maxmonth <= 12);
2054 g_return_if_fail (minday >= 1 && minday <= g_date_get_days_in_month (minmonth, minyear));
2055 g_return_if_fail (maxday >= 1 && maxday <= g_date_get_days_in_month (maxmonth, maxyear));
2056 g_return_if_fail (minyear <= maxyear);
2057 g_return_if_fail (minyear < maxyear || minmonth <= maxmonth);
2058 g_return_if_fail (minyear < maxyear || minmonth < maxmonth || minday <= maxday);
2060 edt->priv->clamp_minyear = minyear;
2061 edt->priv->clamp_minmonth = minmonth;
2062 edt->priv->clamp_minday = minday;
2063 edt->priv->clamp_maxyear = maxyear;
2064 edt->priv->clamp_maxmonth = maxmonth;
2065 edt->priv->clamp_maxday = maxday;
2067 parse_and_update_date (edt);
2069 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
2073 * egg_datetime_set_clamp_time:
2074 * @edt: an #EggDateTime
2075 * @minhour: minimum hour
2076 * @minminute: minimum minute
2077 * @minsecond: minimum second
2078 * @maxhour: maximum hour
2079 * @maxminute: maximum minute
2080 * @maxsecond: maximum second
2082 * Limits the allowed times to the range given. If lazy mode is turned
2083 * off, times that are outside of this range are snapped to the minimum or
2084 * maximum time. Otherwise such times return an invalid value.
2086 void
2087 egg_datetime_set_clamp_time (EggDateTime *edt, guint8 minhour, guint8 minminute, guint8 minsecond, guint8 maxhour, guint8 maxminute, guint8 maxsecond)
2089 g_return_if_fail (minhour <= 23 && maxhour <= 23);
2090 g_return_if_fail (minminute <= 59 && maxminute <= 59);
2091 g_return_if_fail (minsecond <= 59 && maxsecond <= 59);
2092 g_return_if_fail (minhour <= maxhour);
2093 g_return_if_fail (minhour < maxhour || minminute <= maxminute);
2094 g_return_if_fail (minhour < maxhour || minminute < maxminute || minsecond <= maxsecond);
2096 edt->priv->clamp_minhour = minhour;
2097 edt->priv->clamp_minminute = minminute;
2098 edt->priv->clamp_minsecond = minsecond;
2099 edt->priv->clamp_maxhour = maxhour;
2100 edt->priv->clamp_maxminute = maxminute;
2101 edt->priv->clamp_maxsecond = maxsecond;
2103 clamp_time_labels (edt);
2104 parse_and_update_time (edt);
2106 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_TIME_CHANGED], 0);
2110 * egg_datetime_set_clamp_time_t:
2111 * @edt: an #EggDateTime
2113 * Clamps the allowed dates of the widget to valid #time_t values.
2114 * The time clamp settings are not changed.
2116 void
2117 egg_datetime_set_clamp_time_t (EggDateTime *edt)
2119 time_t t;
2120 struct tm start_tm, end_tm;
2121 guint bits;
2122 guint16 year;
2123 guint8 month, day;
2125 g_return_if_fail (edt != NULL);
2126 g_return_if_fail (EGG_IS_DATETIME (edt));
2128 t = 0;
2129 gmtime_r (&t, &start_tm);
2131 /* evil hack */
2132 bits = time_t_bits ();
2133 t = ~0;
2134 t &= ~(1 << (bits - 1));
2136 gmtime_r (&t, &end_tm);
2138 /* Subtract one day from the end date, since not all times of
2139 * the last day can be represented.
2142 year = end_tm.tm_year + 1900;
2143 month = end_tm.tm_mon + 1;
2144 day = end_tm.tm_mday;
2146 if (--day == 0) {
2147 if (--month == 0) {
2148 year--;
2149 month = 12;
2152 day = g_date_get_days_in_month (month, year);
2155 egg_datetime_set_clamp_date (edt, start_tm.tm_year + 1900, start_tm.tm_mon + 1, start_tm.tm_mday, year, month, day);
2159 * egg_datetime_get_clamp_date:
2160 * @edt: an #EggDateTime
2161 * @minyear: #guint16 pointer or %NULL
2162 * @minmonth: #guint8 pointer or %NULL
2163 * @minday: #guint8 pointer or %NULL
2164 * @maxyear: #guint16 pointer or %NULL
2165 * @maxmonth: #guint8 pointer or %NULL
2166 * @maxday: #guint8 pointer or %NULL
2168 * Returns the current date limit settings.
2170 void
2171 egg_datetime_get_clamp_date (EggDateTime *edt,
2172 GDateYear *minyear,
2173 GDateMonth *minmonth,
2174 GDateDay *minday,
2175 GDateYear *maxyear,
2176 GDateMonth *maxmonth,
2177 GDateDay *maxday)
2179 g_return_if_fail (edt != NULL);
2180 g_return_if_fail (EGG_IS_DATETIME (edt));
2182 if (minyear)
2183 *minyear = edt->priv->clamp_minyear;
2184 if (minmonth)
2185 *minmonth = edt->priv->clamp_minmonth;
2186 if (minday)
2187 *minday = edt->priv->clamp_minday;
2188 if (maxyear)
2189 *maxyear = edt->priv->clamp_maxyear;
2190 if (maxmonth)
2191 *maxmonth = edt->priv->clamp_maxmonth;
2192 if (maxday)
2193 *maxday = edt->priv->clamp_maxday;
2197 * egg_datetime_get_clamp_time:
2198 * @edt: an #EggDateTime
2199 * @minhour: #guint8 pointer or %NULL
2200 * @minminute: #guint8 pointer or %NULL
2201 * @minsecond: #guint8 pointer or %NULL
2202 * @maxhour: #guint8 pointer or %NULL
2203 * @maxminute: #guint8 pointer or %NULL
2204 * @maxsecond: #guint8 pointer or %NULL
2206 * Returns the current time limit settings.
2208 void
2209 egg_datetime_get_clamp_time (EggDateTime *edt, guint8 *minhour, guint8 *minminute, guint8 *minsecond, guint8 *maxhour, guint8 *maxminute, guint8 *maxsecond)
2211 g_return_if_fail (edt != NULL);
2212 g_return_if_fail (EGG_IS_DATETIME (edt));
2214 if (minhour)
2215 *minhour = edt->priv->clamp_minhour;
2216 if (minminute)
2217 *minminute = edt->priv->clamp_minminute;
2218 if (minsecond)
2219 *minsecond = edt->priv->clamp_minsecond;
2220 if (maxhour)
2221 *maxhour = edt->priv->clamp_maxhour;
2222 if (maxminute)
2223 *maxminute = edt->priv->clamp_maxminute;
2224 if (maxsecond)
2225 *maxsecond = edt->priv->clamp_maxsecond;
2229 * egg_datetime_get_date_layout:
2230 * @edt: an #EggDateTime
2232 * Gets the PangoLayout used to display the date. See gtk_entry_get_layout()
2233 * for more information. The returned layout is owned by the date/time
2234 * widget so need not be freed by the caller.
2236 * Return value: the #PangoLayout for this widget's date part
2238 PangoLayout *
2239 egg_datetime_get_date_layout (EggDateTime *edt)
2241 g_return_val_if_fail (edt != NULL, NULL);
2242 g_return_val_if_fail (EGG_IS_DATETIME (edt), NULL);
2244 return gtk_entry_get_layout (GTK_ENTRY (edt->priv->date_entry));
2248 * egg_datetime_get_time_layout:
2249 * @edt: an #EggDateTime
2251 * Gets the PangoLayout used to display the time. See gtk_entry_get_layout()
2252 * for more information. The returned layout is owned by the date/time
2253 * widget so need not be freed by the caller.
2255 * Return value: the #PangoLayout for this widget's time part
2257 PangoLayout *
2258 egg_datetime_get_time_layout (EggDateTime *edt)
2260 g_return_val_if_fail (edt != NULL, NULL);
2261 g_return_val_if_fail (EGG_IS_DATETIME (edt), NULL);
2263 return gtk_entry_get_layout (GTK_ENTRY (edt->priv->time_entry));
2266 /**************************************************************************/
2268 /* This is a private time list widget implementation for use as time popup.
2271 static GtkWidget *
2272 timelist_new (EggDateTime *edt)
2274 GtkWidget *timelist;
2275 GtkWidget *list;
2276 GtkListStore *model;
2277 GtkTreeSelection *selection;
2278 GtkCellRenderer *renderer;
2280 timelist = gtk_scrolled_window_new (NULL, NULL);
2281 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (timelist),
2282 GTK_POLICY_NEVER,
2283 GTK_POLICY_ALWAYS);
2285 model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_UINT);
2286 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
2287 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
2288 gtk_widget_show (list);
2290 renderer = gtk_cell_renderer_text_new ();
2291 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (list),
2293 _("Time"),
2294 renderer,
2295 "text", 0,
2296 NULL);
2298 gtk_container_add (GTK_CONTAINER (timelist), list);
2300 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
2301 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
2302 g_signal_connect_swapped(G_OBJECT(list), "row-activated", G_CALLBACK(time_popup_hide), edt);
2303 timelist_set_list (TIMELIST (timelist), 00, 00, 23, 59);
2305 return timelist;
2308 static void
2309 timelist_set_list (Timelist *timelist,
2310 guint8 minhour, guint8 minminute,
2311 guint8 maxhour, guint8 maxminute)
2313 GtkWidget *tree = gtk_bin_get_child (GTK_BIN (timelist));
2314 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
2315 gint minidx, maxidx;
2316 GtkTreeIter iter;
2317 gint i;
2319 minidx = minhour * 2 + (minminute + 29) / 30;
2320 maxidx = maxhour * 2 + (maxminute + 29) / 30;
2322 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
2323 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2324 0, _("no end time"),
2325 1, -1,
2326 -1);
2328 for (i = minidx; i < maxidx; i++) {
2329 gchar *s;
2330 guint hour, minute;
2332 hour = i / 2;
2333 minute = (i % 2) * 30;
2335 s = get_time_string (hour, minute, 0xff);
2337 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
2338 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2339 0, s,
2340 1, hour * 100 + minute,
2341 -1);
2343 g_free (s);
2347 static void
2348 timelist_set_time (Timelist *timelist, gint hour, gint minute)
2350 GtkWidget *tree = gtk_bin_get_child (GTK_BIN (timelist));
2351 GtkTreeModel *model;
2352 GtkTreeSelection *selection;
2353 GtkTreeIter iter;
2355 model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
2356 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
2358 if (!gtk_tree_model_get_iter_first (model, &iter))
2360 return;
2363 do {
2364 guint time;
2365 gtk_tree_model_get (model, &iter, 1, &time, -1);
2367 if (time / 100 == hour && time % 100 == minute) {
2368 gtk_tree_selection_select_iter (selection, &iter);
2369 return;
2372 } while (gtk_tree_model_iter_next (model, &iter));
2374 gtk_tree_selection_unselect_all (selection);
2377 static gboolean
2378 timelist_get_time (Timelist *timelist, gint *hour, gint *minute)
2380 GtkWidget *tree = gtk_bin_get_child (GTK_BIN (timelist));
2381 GtkTreeSelection *selection;
2382 GtkTreeModel *model;
2383 GtkTreeIter iter;
2384 guint time;
2386 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
2388 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
2390 return FALSE;
2393 gtk_tree_model_get (model, &iter, 1, &time, -1);
2395 if(time == -1)
2397 *hour = -1;
2398 *minute = 0;
2400 else
2402 if (hour)
2403 *hour = time / 100;
2404 if (minute)
2405 *minute = time % 100;
2407 return TRUE;
2410 static void
2411 timelist_clamp (Timelist *timelist,
2412 guint8 minhour, guint8 minminute,
2413 guint8 maxhour, guint8 maxminute)
2415 timelist_set_list (timelist, minhour, minminute, maxhour, maxminute);
2418 static void
2419 timelist_selection_cb (Timelist *timelist, GtkTreeSelection *selection)
2421 void (*cb)(gpointer, Timelist *);
2422 gpointer data;
2424 cb = g_object_get_data (G_OBJECT (selection), "cb");
2425 data = g_object_get_data (G_OBJECT (selection), "data");
2427 cb (data, timelist);
2430 static void
2431 timelist_set_selection_callback (Timelist *timelist, void (*cb)(void), gpointer data)
2433 GtkWidget *tree = gtk_bin_get_child (GTK_BIN (timelist));
2434 GtkTreeSelection *selection;
2436 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
2438 g_object_set_data (G_OBJECT (selection), "cb", cb);
2439 g_object_set_data (G_OBJECT (selection), "data", data);
2440 g_signal_connect_swapped (G_OBJECT (selection), "changed",
2441 G_CALLBACK (timelist_selection_cb), timelist);