2006-11-14 Günther Brammer <GBrammer@gmx.de>
[anjuta-git-plugin.git] / plugins / gtodo / egg-datetime.c
blob5bfb319dbeef5d0c5cdfb84b7e8d041601f2d076
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., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, 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 pspec = g_param_spec_uint ("second",
338 _("Second"),
339 _("Displayed second"),
340 0, 59, 0,
341 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
342 g_object_class_install_property (o_class, ARG_SECOND, pspec);
343 pspec = g_param_spec_uint ("clamp-minyear",
344 _("Lower limit year"),
345 _("Year part of the lower date limit"),
346 1, 9999, 1,
347 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
348 g_object_class_install_property (o_class, ARG_CLAMP_MINYEAR, pspec);
349 pspec = g_param_spec_uint ("clamp-maxyear",
350 _("Upper limit year"),
351 _("Year part of the upper date limit"),
352 1, 9999, 9999,
353 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
354 g_object_class_install_property (o_class, ARG_CLAMP_MAXYEAR, pspec);
355 pspec = g_param_spec_uint ("clamp-minmonth",
356 _("Lower limit month"),
357 _("Month part of the lower date limit"),
358 G_DATE_JANUARY, G_DATE_DECEMBER, G_DATE_JANUARY,
359 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
360 g_object_class_install_property (o_class, ARG_CLAMP_MINMONTH, pspec);
361 pspec = g_param_spec_uint ("clamp-maxmonth",
362 _("Upper limit month"),
363 _("Month part of the upper date limit"),
364 G_DATE_JANUARY, G_DATE_DECEMBER, G_DATE_DECEMBER,
365 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
366 g_object_class_install_property (o_class, ARG_CLAMP_MAXMONTH, pspec);
367 pspec = g_param_spec_uint ("clamp-minday",
368 _("Lower limit day"),
369 _("Day of month part of the lower date limit"),
370 1, 31, 1,
371 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
372 g_object_class_install_property (o_class, ARG_CLAMP_MINDAY, pspec);
373 pspec = g_param_spec_uint ("clamp-maxday",
374 _("Upper limit day"),
375 _("Day of month part of the upper date limit"),
376 1, 31, 31,
377 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
378 g_object_class_install_property (o_class, ARG_CLAMP_MAXDAY, pspec);
379 pspec = g_param_spec_uint ("clamp-minhour",
380 _("Lower limit hour"),
381 _("Hour part of the lower time limit"),
382 0, 23, 0,
383 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
384 g_object_class_install_property (o_class, ARG_CLAMP_MINHOUR, pspec);
385 pspec = g_param_spec_uint ("clamp-maxhour",
386 _("Upper limit hour"),
387 _("Hour part of the upper time limit"),
388 0, 23, 23,
389 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
390 g_object_class_install_property (o_class, ARG_CLAMP_MAXHOUR, pspec);
391 pspec = g_param_spec_uint ("clamp-minminute",
392 _("Lower limit minute"),
393 _("Minute part of the lower time limit"),
394 0, 59, 0,
395 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
396 g_object_class_install_property (o_class, ARG_CLAMP_MINMINUTE, pspec);
397 pspec = g_param_spec_uint ("clamp-maxminute",
398 _("Upper limit minute"),
399 _("Minute part of the upper time limit"),
400 0, 59, 59,
401 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
402 g_object_class_install_property (o_class, ARG_CLAMP_MAXMINUTE, pspec);
403 pspec = g_param_spec_uint ("clamp-minsecond",
404 _("Lower limit second"),
405 _("Second part of the lower time limit"),
406 0, 59, 0,
407 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
408 g_object_class_install_property (o_class, ARG_CLAMP_MINSECOND, pspec);
409 pspec = g_param_spec_uint ("clamp-maxsecond",
410 _("Upper limit second"),
411 _("Second part of the upper time limit"),
412 0, 59, 59,
413 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
414 g_object_class_install_property (o_class, ARG_CLAMP_MAXSECOND, pspec);
416 /* Signals */
418 egg_datetime_signals [SIGNAL_DATE_CHANGED] =
419 g_signal_new ("date-changed",
420 G_TYPE_FROM_CLASS (klass),
421 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
422 G_STRUCT_OFFSET (EggDateTimeClass, date_changed),
423 NULL, NULL,
424 g_cclosure_marshal_VOID__VOID,
425 G_TYPE_NONE, 0);
427 egg_datetime_signals [SIGNAL_TIME_CHANGED] =
428 g_signal_new ("time-changed",
429 G_TYPE_FROM_CLASS (klass),
430 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
431 G_STRUCT_OFFSET (EggDateTimeClass, time_changed),
432 NULL, NULL,
433 g_cclosure_marshal_VOID__VOID,
434 G_TYPE_NONE, 0);
437 static void
438 egg_datetime_init (EggDateTime *edt)
440 EggDateTimePrivate *priv;
441 GtkWidget *arrow, *hbox, *vbox, *frame;
442 GtkCalendarDisplayOptions cal_options;
443 priv = g_new0 (EggDateTimePrivate, 1);
444 edt->priv = priv;
446 /* Translators: Change this if you want to start weeks at mondays. */
447 priv->week_start_monday = strcmp (_("week-starts-monday: yes"), "week-starts-monday: yes") == 0 ? TRUE : FALSE;
449 priv->date_valid = FALSE;
450 priv->time_valid = FALSE;
452 /* Initialize Widgets */
454 gtk_box_set_spacing (GTK_BOX (edt), 4);
456 /* Date Widgets */
458 priv->date_box = gtk_hbox_new (FALSE, 0);
459 gtk_box_pack_start (GTK_BOX (edt), priv->date_box, TRUE, TRUE, 0);
461 priv->date_entry = gtk_entry_new ();
462 gtk_entry_set_width_chars (GTK_ENTRY (priv->date_entry), 13);
464 _add_atk_name_desc (priv->date_entry, _("Date"), _("Enter the date directly"));
466 g_signal_connect_swapped (G_OBJECT (priv->date_entry), "focus-out-event",
467 G_CALLBACK (date_focus_out), edt);
468 gtk_widget_show (priv->date_entry);
469 gtk_box_pack_start (GTK_BOX (priv->date_box), priv->date_entry, TRUE, TRUE, 0);
471 priv->date_button = gtk_toggle_button_new ();
473 _add_atk_name_desc (priv->date_button, _("Select Date"), _("Select the date from a calendar"));
475 gtk_box_pack_start (GTK_BOX (priv->date_box), priv->date_button, FALSE, FALSE, 0);
476 arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
477 gtk_container_add (GTK_CONTAINER (priv->date_button), arrow);
478 gtk_widget_show (arrow);
480 g_signal_connect_swapped (G_OBJECT (priv->date_button), "toggled",
481 G_CALLBACK (date_arrow_toggled), edt);
483 _add_atk_relation (priv->date_button, priv->date_entry,
484 ATK_RELATION_CONTROLLER_FOR, ATK_RELATION_CONTROLLED_BY);
486 /* Time Widgets */
488 priv->time_box = gtk_hbox_new (FALSE, 0);
489 gtk_box_pack_start (GTK_BOX (edt), priv->time_box, TRUE, TRUE, 0);
491 priv->time_entry = gtk_entry_new ();
492 gtk_entry_set_width_chars (GTK_ENTRY (priv->time_entry), 10);
494 _add_atk_name_desc (priv->time_entry, _("Time"), _("Enter the time directly"));
496 g_signal_connect_swapped (G_OBJECT (priv->time_entry), "focus-out-event",
497 G_CALLBACK (time_focus_out), edt);
498 gtk_widget_show (priv->time_entry);
499 gtk_box_pack_start (GTK_BOX (priv->time_box), priv->time_entry, TRUE, TRUE, 0);
501 priv->time_button = gtk_toggle_button_new ();
503 _add_atk_name_desc (priv->date_button, _("Select Time"), _("Select the time from a list"));
505 gtk_box_pack_start (GTK_BOX (priv->time_box), priv->time_button, FALSE, FALSE, 0);
506 arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
507 gtk_container_add (GTK_CONTAINER (priv->time_button), arrow);
508 gtk_widget_show (arrow);
509 g_signal_connect_swapped (G_OBJECT (priv->time_button), "toggled",
510 G_CALLBACK (time_arrow_toggled), edt);
512 _add_atk_relation (priv->time_button, priv->time_entry,
513 ATK_RELATION_CONTROLLER_FOR, ATK_RELATION_CONTROLLED_BY);
515 /* Calendar Popup */
517 priv->cal_popup = gtk_window_new (GTK_WINDOW_POPUP);
518 gtk_widget_set_events (priv->cal_popup,
519 gtk_widget_get_events (priv->cal_popup) | GDK_KEY_PRESS_MASK);
520 gtk_window_set_resizable (GTK_WINDOW (priv->cal_popup), FALSE);
521 g_signal_connect_swapped (G_OBJECT (priv->cal_popup), "delete-event",
522 G_CALLBACK (cal_popup_closed), edt);
523 g_signal_connect_swapped (G_OBJECT (priv->cal_popup), "key-press-event",
524 G_CALLBACK (cal_popup_key_pressed), edt);
525 g_signal_connect_swapped (G_OBJECT (priv->cal_popup), "button-press-event",
526 G_CALLBACK (cal_popup_button_pressed), edt);
528 vbox = gtk_vbox_new(FALSE, 0);
529 frame = gtk_frame_new(NULL);
530 priv->calendar = gtk_calendar_new ();
531 gtk_window_set_focus(GTK_WINDOW(priv->cal_popup), priv->calendar);
532 gtk_container_add (GTK_CONTAINER (frame), vbox);
533 gtk_container_add (GTK_CONTAINER (priv->cal_popup), frame);
534 cal_options = GTK_CALENDAR_SHOW_DAY_NAMES | GTK_CALENDAR_SHOW_HEADING;
535 if (priv->week_start_monday)
536 cal_options |= GTK_CALENDAR_WEEK_START_MONDAY;
537 gtk_calendar_display_options (GTK_CALENDAR (priv->calendar), cal_options);
538 gtk_box_pack_start(GTK_BOX(vbox), priv->calendar, TRUE, TRUE,0);
539 g_signal_connect_swapped (G_OBJECT (priv->calendar), "day-selected",
540 G_CALLBACK (cal_popup_changed), edt);
541 g_signal_connect_swapped (G_OBJECT (priv->calendar), "day-selected-double-click",
542 G_CALLBACK (cal_popup_double_click), edt);
543 hbox = gtk_hbox_new(FALSE, 0);
544 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE,0);
545 priv->today = gtk_button_new_with_mnemonic(_("_Today"));
546 priv->nodate = gtk_button_new_with_mnemonic(_("_No Date"));
547 gtk_box_pack_start(GTK_BOX(hbox), priv->today, TRUE, TRUE,0);
548 gtk_box_pack_start(GTK_BOX(hbox), priv->nodate, TRUE, TRUE,0);
549 g_signal_connect_swapped (G_OBJECT (priv->today), "clicked",
550 G_CALLBACK (cal_set_today), edt);
551 g_signal_connect_swapped (G_OBJECT (priv->nodate), "clicked",
552 G_CALLBACK (cal_set_nodate), edt);
555 gtk_widget_show_all(frame);
556 gtk_widget_show(priv->calendar);
557 /* Time Popup */
559 priv->time_popup = gtk_window_new (GTK_WINDOW_POPUP);
560 gtk_widget_set_events (priv->time_popup,
561 gtk_widget_get_events (priv->time_popup) | GDK_KEY_PRESS_MASK);
562 gtk_window_set_resizable (GTK_WINDOW (priv->time_popup), FALSE);
563 g_signal_connect_swapped (G_OBJECT (priv->time_popup), "delete-event",
564 G_CALLBACK (time_popup_closed), edt);
565 g_signal_connect_swapped (G_OBJECT (priv->time_popup), "key-press-event",
566 G_CALLBACK (time_popup_key_pressed), edt);
567 g_signal_connect_swapped (G_OBJECT (priv->time_popup), "button-press-event",
568 G_CALLBACK (time_popup_button_pressed), edt);
570 priv->timelist = timelist_new (edt);
572 gtk_window_set_default(GTK_WINDOW(priv->time_popup), gtk_bin_get_child(GTK_BIN(priv->timelist)));
574 timelist_set_selection_callback (TIMELIST (priv->timelist), G_CALLBACK (time_popup_changed), edt);
575 gtk_widget_set_size_request (priv->timelist, -1, 400);
576 gtk_container_add (GTK_CONTAINER (priv->time_popup), priv->timelist);
577 gtk_widget_show (priv->timelist);
578 if(priv->no_date) gtk_widget_set_sensitive(priv->time_box, FALSE);
581 static void
582 egg_datetime_set_property (GObject *object,
583 guint property_id,
584 const GValue *value,
585 GParamSpec *pspec)
587 EggDateTime *edt;
588 EggDateTimePrivate *priv;
590 g_return_if_fail (object != NULL);
591 g_return_if_fail (EGG_IS_DATETIME (object));
593 edt = EGG_DATETIME (object);
594 priv = edt->priv;
596 switch (property_id) {
597 case ARG_DISPLAY_MODE:
598 egg_datetime_set_display_mode (edt, g_value_get_uint (value));
599 break;
600 case ARG_LAZY:
601 egg_datetime_set_lazy (edt, g_value_get_boolean (value));
602 break;
603 case ARG_YEAR:
604 priv->year = g_value_get_uint (value);
605 break;
606 case ARG_MONTH:
607 priv->minute = g_value_get_uint (value);
608 break;
609 case ARG_DAY:
610 priv->day = g_value_get_uint (value);
611 break;
612 case ARG_HOUR:
613 priv->hour = g_value_get_uint (value);
614 break;
615 case ARG_MINUTE:
616 priv->minute = g_value_get_uint (value);
617 break;
618 case ARG_SECOND:
619 priv->second = g_value_get_uint (value);
620 break;
621 case ARG_CLAMP_MINYEAR:
622 priv->clamp_minyear = g_value_get_uint (value);
623 break;
624 case ARG_CLAMP_MINMONTH:
625 priv->clamp_minmonth = g_value_get_uint (value);
626 break;
627 case ARG_CLAMP_MINDAY:
628 priv->clamp_minday = g_value_get_uint (value);
629 break;
630 case ARG_CLAMP_MINHOUR:
631 priv->clamp_minhour = g_value_get_uint (value);
632 break;
633 case ARG_CLAMP_MINMINUTE:
634 priv->clamp_minminute = g_value_get_uint (value);
635 break;
636 case ARG_CLAMP_MINSECOND:
637 priv->clamp_minsecond = g_value_get_uint (value);
638 break;
639 case ARG_CLAMP_MAXYEAR:
640 priv->clamp_maxyear = g_value_get_uint (value);
641 break;
642 case ARG_CLAMP_MAXMONTH:
643 priv->clamp_maxmonth = g_value_get_uint (value);
644 break;
645 case ARG_CLAMP_MAXDAY:
646 priv->clamp_maxday = g_value_get_uint (value);
647 break;
648 case ARG_CLAMP_MAXHOUR:
649 priv->clamp_maxhour = g_value_get_uint (value);
650 break;
651 case ARG_CLAMP_MAXMINUTE:
652 priv->clamp_maxminute = g_value_get_uint (value);
653 break;
654 case ARG_CLAMP_MAXSECOND:
655 priv->clamp_maxsecond = g_value_get_uint (value);
656 break;
660 static void
661 egg_datetime_get_property (GObject *object,
662 guint property_id,
663 GValue *value,
664 GParamSpec *pspec)
666 EggDateTimePrivate *priv;
668 g_return_if_fail (object != NULL);
669 g_return_if_fail (EGG_IS_DATETIME (object));
671 priv = EGG_DATETIME (object)->priv;
673 switch (property_id) {
674 case ARG_DISPLAY_MODE:
675 g_value_set_uint (value, (guint) priv->display_mode);
676 break;
677 case ARG_LAZY:
678 g_value_set_boolean (value, priv->lazy);
679 break;
680 case ARG_YEAR:
681 g_value_set_uint (value, priv->year);
682 break;
683 case ARG_MONTH:
684 g_value_set_uint (value, priv->month);
685 break;
686 case ARG_DAY:
687 g_value_set_uint (value, priv->day);
688 break;
689 case ARG_HOUR:
690 g_value_set_uint (value, priv->hour);
691 break;
692 case ARG_MINUTE:
693 g_value_set_uint (value, priv->minute);
694 break;
695 case ARG_SECOND:
696 g_value_set_uint (value, priv->second);
697 break;
698 case ARG_CLAMP_MINYEAR:
699 g_value_set_uint (value, priv->clamp_minyear);
700 break;
701 case ARG_CLAMP_MINMONTH:
702 g_value_set_uint (value, priv->clamp_minmonth);
703 break;
704 case ARG_CLAMP_MINDAY:
705 g_value_set_uint (value, priv->clamp_minday);
706 break;
707 case ARG_CLAMP_MINHOUR:
708 g_value_set_uint (value, priv->clamp_minhour);
709 break;
710 case ARG_CLAMP_MINMINUTE:
711 g_value_set_uint (value, priv->clamp_minminute);
712 break;
713 case ARG_CLAMP_MINSECOND:
714 g_value_set_uint (value, priv->clamp_minsecond);
715 break;
716 case ARG_CLAMP_MAXYEAR:
717 g_value_set_uint (value, priv->clamp_maxyear);
718 break;
719 case ARG_CLAMP_MAXMONTH:
720 g_value_set_uint (value, priv->clamp_maxmonth);
721 break;
722 case ARG_CLAMP_MAXDAY:
723 g_value_set_uint (value, priv->clamp_maxday);
724 break;
725 case ARG_CLAMP_MAXHOUR:
726 g_value_set_uint (value, priv->clamp_maxhour);
727 break;
728 case ARG_CLAMP_MAXMINUTE:
729 g_value_set_uint (value, priv->clamp_maxminute);
730 break;
731 case ARG_CLAMP_MAXSECOND:
732 g_value_set_uint (value, priv->clamp_maxsecond);
733 break;
737 static void
738 egg_datetime_destroy (GtkObject *object)
740 EggDateTime *edt = EGG_DATETIME (object);
741 EggDateTimePrivate *priv = edt->priv;
743 if (priv->cal_popup) {
744 gtk_widget_destroy (priv->cal_popup);
745 priv->cal_popup = NULL;
748 if (priv->time_popup) {
749 gtk_widget_destroy (priv->time_popup);
750 priv->time_popup = NULL;
753 if (GTK_OBJECT_CLASS (parent_class)->destroy)
754 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
757 static void
758 egg_datetime_finalize (GObject *object)
760 EggDateTime *edt = EGG_DATETIME (object);
762 g_free (edt->priv);
764 if (G_OBJECT_CLASS (parent_class)->finalize)
765 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
769 * Utility Functions
772 /* Determine the number of bits, time_t uses. */
773 static guint
774 time_t_bits (void) {
775 guint i;
776 time_t t;
778 for (t = 1, i = 0; t != 0; t = t << 1, i++)
781 return i;
784 static gchar *
785 get_time_string (guint8 hour, guint8 minute, guint8 second)
787 gchar *s;
789 /* Translators: set this to anything else if you want to use a
790 * 24 hour clock.
792 if (!strcmp (_("24hr: no"), "24hr: yes")) {
793 const gchar *ampm_s;
795 if (hour < 12)
796 ampm_s = _("AM");
797 else
798 ampm_s = _("PM");
800 hour %= 12;
801 if (hour == 0)
802 hour = 12;
804 if (second <= 59)
805 /* Translators: This is hh:mm:ss AM/PM. */
806 s = g_strdup_printf (_("%02d:%02d:%02d %s"), hour, minute, second, ampm_s);
807 else
808 /* Translators: This is hh:mm AM/PM. */
809 s = g_strdup_printf (_("%02d:%02d %s"), hour, minute, ampm_s);
810 } else {
811 if (second <= 59)
812 /* Translators: This is hh:mm:ss. */
813 s = g_strdup_printf (_("%02d:%02d:%02d"), hour, minute, second);
814 else
815 /* Translators: This is hh:mm. */
816 s = g_strdup_printf (_("%02d:%02d"), hour, minute);
819 return s;
822 static void
823 popup_position (GtkWidget *widget, GtkWindow *popup)
825 GtkRequisition requisition;
826 gint x, y, width, height;
828 gtk_widget_size_request (GTK_WIDGET (popup), &requisition);
829 gdk_window_get_origin (widget->window, &x, &y);
831 x += widget->allocation.x;
832 y += widget->allocation.y;
833 width = widget->allocation.width;
834 height = widget->allocation.height;
836 x += width - requisition.width;
837 y += height;
839 if (x < 0)
840 x = 0;
841 if (y < 0)
842 y = 0;
844 gtk_window_move (popup, x, y);
847 static void
848 popup_show (GtkWindow *popup)
850 /* GdkCursor *cursor;*/
852 gtk_widget_show_all (GTK_WIDGET (popup));
853 gtk_widget_grab_focus (GTK_WIDGET (popup));
854 gtk_grab_add (GTK_WIDGET (popup));
856 /* Think its ugly to change the cursor.. */
858 cursor = gdk_cursor_new (GDK_ARROW);
859 gdk_pointer_grab (GTK_WIDGET (popup)->window, TRUE,
860 (GDK_BUTTON_PRESS_MASK
861 | GDK_BUTTON_RELEASE_MASK
862 | GDK_POINTER_MOTION_MASK),
863 NULL, cursor, GDK_CURRENT_TIME);
864 gdk_cursor_unref (cursor);
868 static void
869 popup_hide (GtkWindow *popup)
871 gtk_widget_hide (GTK_WIDGET (popup));
872 gtk_grab_remove (GTK_WIDGET (popup));
873 gdk_pointer_ungrab (GDK_CURRENT_TIME);
877 * Calendar Popup
880 static void
881 date_arrow_toggled (EggDateTime *edt, GtkToggleButton *button)
883 EggDateTimePrivate *priv = edt->priv;
885 if (!gtk_toggle_button_get_active (button))
886 return;
888 /* Set Date */
890 parse_date (edt);
892 if(priv->nodate == 0)
894 gtk_calendar_select_month (GTK_CALENDAR (priv->calendar), priv->month - 1, priv->year);
895 gtk_calendar_select_day (GTK_CALENDAR (priv->calendar), priv->day);
898 /* Position Popup */
900 popup_position (priv->date_button, GTK_WINDOW (priv->cal_popup));
902 /* Show Popup */
904 popup_show (GTK_WINDOW (priv->cal_popup));
907 static void
908 cal_popup_changed (EggDateTime *edt, GtkCalendar *calendar)
910 guint year, month, day;
911 gtk_calendar_get_date (GTK_CALENDAR (edt->priv->calendar), &year, &month, &day);
913 edt->priv->no_date = FALSE;
915 edt->priv->year = year;
916 edt->priv->month = month + 1;
917 edt->priv->day = day;
918 edt->priv->date_valid = TRUE;
920 normalize_date (edt);
921 update_date_label (edt);
923 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
926 static void
927 cal_set_today (EggDateTime *edt, GtkCalendar *calendar)
929 GDate *now;
930 guint year, month, day;
931 edt->priv->no_date = FALSE;
933 now = g_date_new();
934 g_date_set_time (now, time (NULL));
936 year = g_date_get_year(now);
937 month = g_date_get_month(now);
938 day = g_date_get_day(now);
940 edt->priv->year = year;
941 edt->priv->month = month;
942 edt->priv->day = day;
943 edt->priv->date_valid = TRUE;
945 gtk_calendar_select_month(GTK_CALENDAR (edt->priv->calendar), month-1, year);
946 gtk_calendar_select_day(GTK_CALENDAR (edt->priv->calendar), day);
947 gtk_widget_show_all(edt->priv->calendar);
949 gtk_calendar_set_date (GTK_CALENDAR (edt->priv->calendar), &year, &month, &day);
951 normalize_date (edt);
952 update_date_label (edt);
954 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
955 cal_popup_hide(edt);
956 g_date_free(now);
960 static void
961 cal_set_nodate (EggDateTime *edt, GtkCalendar *calendar)
963 edt->priv->no_date = TRUE;
965 normalize_date (edt);
966 update_date_label (edt);
968 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
969 cal_popup_hide(edt);
973 void egg_set_nodate(EggDateTime *edt, gboolean val)
975 edt->priv->no_date = val;
977 normalize_date (edt);
978 update_date_label (edt);
980 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
984 gboolean egg_get_nodate(EggDateTime *edt)
986 return edt->priv->no_date;
990 static void
991 cal_popup_double_click (EggDateTime *edt, GtkCalendar *calendar)
993 edt->priv->no_date = FALSE;
995 gtk_widget_set_sensitive(priv->time_box, edt->priv->no_date);
997 cal_popup_hide (edt);
1000 static gboolean
1001 cal_popup_key_pressed (EggDateTime *edt, GdkEventKey *event, GtkWidget *widget)
1003 if (event->keyval != GDK_Escape)
1004 return FALSE;
1006 g_signal_stop_emission_by_name (G_OBJECT (widget), "key_press_event");
1008 cal_popup_hide (edt);
1010 return TRUE;
1013 static gboolean
1014 cal_popup_button_pressed (EggDateTime *edt, GdkEventButton *event, GtkWidget *widget)
1016 GtkWidget *child;
1018 child = gtk_get_event_widget ((GdkEvent *) event);
1020 /* We don't ask for button press events on the grab widget, so
1021 * if an event is reported directly to the grab widget, it must
1022 * be on a window outside the application (and thus we remove
1023 * the popup window). Otherwise, we check if the widget is a child
1024 * of the grab widget, and only remove the popup window if it
1025 * is not.
1027 if (child != widget) {
1028 while (child) {
1029 if (child == widget)
1030 return FALSE;
1031 child = child->parent;
1035 cal_popup_hide (edt);
1037 return TRUE;
1040 static gboolean
1041 cal_popup_closed (EggDateTime *edt, GtkWindow *popup)
1043 cal_popup_hide (edt);
1045 return TRUE;
1048 static void
1049 cal_popup_hide (EggDateTime *edt)
1051 EggDateTimePrivate *priv = edt->priv;
1053 popup_hide (GTK_WINDOW (priv->cal_popup));
1055 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->date_button), FALSE);
1056 gtk_widget_grab_focus (priv->date_entry);
1058 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
1062 * Time Popup
1065 static void
1066 time_arrow_toggled (EggDateTime *edt, GtkToggleButton *button)
1068 EggDateTimePrivate *priv = edt->priv;
1069 gint hour, minute;
1070 if (!gtk_toggle_button_get_active (button))
1071 return;
1073 /* Set Time */
1074 parse_time (edt);
1076 /* Position Popup */
1078 popup_position (priv->time_button, GTK_WINDOW (priv->time_popup));
1080 /* Show Popup */
1081 /* dirty hack to stop it selecting the first item when showing for the first time in new window*/
1082 /* I should fix this.. because this is quiet nasty */
1083 hour = priv->hour;
1084 minute= priv->minute;
1085 popup_show (GTK_WINDOW (priv->time_popup));
1086 priv->hour = hour;
1087 priv->minute = minute;
1088 timelist_set_time (TIMELIST (priv->timelist), priv->hour, priv->minute);
1089 update_time_label(edt);
1092 static void
1093 time_popup_changed (EggDateTime *edt, Timelist *timelist)
1095 EggDateTimePrivate *priv = edt->priv;
1097 if (!timelist_get_time (timelist, &priv->hour, &priv->minute))
1098 return;
1100 priv->second = 0;
1101 priv->time_valid = TRUE;
1103 normalize_time (edt);
1105 if(GTK_WIDGET_VISIBLE(timelist))update_time_label (edt);
1107 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_TIME_CHANGED], 0);
1110 static gboolean
1111 time_popup_key_pressed (EggDateTime *edt, GdkEventKey *event, GtkWidget *widget)
1113 if (event->keyval != GDK_Escape)
1114 return FALSE;
1116 g_signal_stop_emission_by_name (G_OBJECT (widget), "key_press_event");
1118 time_popup_hide (edt);
1120 return TRUE;
1123 static gboolean
1124 time_popup_button_pressed (EggDateTime *edt, GdkEventButton *event, GtkWidget *widget)
1126 GtkWidget *child;
1128 child = gtk_get_event_widget ((GdkEvent *) event);
1130 /* We don't ask for button press events on the grab widget, so
1131 * if an event is reported directly to the grab widget, it must
1132 * be on a window outside the application (and thus we remove
1133 * the popup window). Otherwise, we check if the widget is a child
1134 * of the grab widget, and only remove the popup window if it
1135 * is not.
1137 if (child != widget) {
1138 while (child) {
1139 if (child == widget)
1140 return FALSE;
1141 child = child->parent;
1145 time_popup_hide (edt);
1147 return TRUE;
1150 static gboolean
1151 time_popup_closed (EggDateTime *edt, GtkWindow *popup)
1153 time_popup_hide (edt);
1155 return TRUE;
1158 static void
1159 time_popup_hide (EggDateTime *edt)
1161 EggDateTimePrivate *priv = edt->priv;
1162 popup_hide (GTK_WINDOW (priv->time_popup));
1164 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->time_button), FALSE);
1165 gtk_widget_grab_focus (priv->time_entry);
1167 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_TIME_CHANGED], 0);
1171 * Callbacks
1174 static gboolean
1175 date_focus_out (EggDateTime *edt, GtkEntry *entry)
1177 parse_date (edt);
1178 update_date_label (edt);
1180 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
1182 return FALSE;
1185 static gboolean
1186 time_focus_out (EggDateTime *edt, GtkEntry *entry)
1188 parse_time (edt);
1189 update_time_label (edt);
1191 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_TIME_CHANGED], 0);
1193 return FALSE;
1197 * Private Methods
1200 static void
1201 apply_display_mode (EggDateTime *edt)
1203 if (edt->priv->display_mode & EGG_DATETIME_DISPLAY_DATE)
1204 gtk_widget_show (edt->priv->date_box);
1205 else
1206 gtk_widget_hide (edt->priv->date_box);
1207 if (edt->priv->display_mode & EGG_DATETIME_DISPLAY_MONTH)
1208 gtk_widget_show (edt->priv->date_button);
1209 else
1210 gtk_widget_hide (edt->priv->date_button);
1212 if (edt->priv->display_mode & EGG_DATETIME_DISPLAY_TIME)
1213 gtk_widget_show (edt->priv->time_box);
1214 else
1215 gtk_widget_hide (edt->priv->time_box);
1216 if (edt->priv->display_mode & EGG_DATETIME_DISPLAY_HOUR)
1217 gtk_widget_show (edt->priv->time_button);
1218 else
1219 gtk_widget_hide (edt->priv->time_button);
1222 static void
1223 clamp_time_labels (EggDateTime *edt)
1225 EggDateTimePrivate *priv = edt->priv;
1227 timelist_clamp (TIMELIST (priv->timelist),
1228 priv->clamp_minhour,
1229 priv->clamp_minminute,
1230 priv->clamp_maxhour,
1231 priv->clamp_maxminute);
1234 /* Updates the date entry to the current date. */
1235 static void
1236 update_date_label (EggDateTime *edt)
1238 EggDateTimePrivate *priv = edt->priv;
1239 gchar *s;
1240 gtk_widget_set_sensitive(priv->time_box, !priv->no_date);
1241 if(priv->no_date)
1243 gtk_entry_set_text(GTK_ENTRY(priv->date_entry), _("No Date"));
1244 return;
1247 if (!priv->date_valid) {
1248 gtk_entry_set_text (GTK_ENTRY (priv->date_entry), "");
1249 return;
1252 /* TODO: should handle other display modes as well... */
1254 /* Translators: This is yyyy-mm-dd. */
1255 s = g_strdup_printf (_("%04d-%02d-%02d"), priv->year, priv->month, priv->day);
1256 gtk_entry_set_text (GTK_ENTRY (priv->date_entry), s);
1257 g_free (s);
1260 /* Updates the time entry to the current time. */
1261 static void
1262 update_time_label (EggDateTime *edt)
1264 EggDateTimePrivate *priv = edt->priv;
1265 gchar *s;
1267 if (!priv->time_valid) {
1268 gtk_entry_set_text (GTK_ENTRY (priv->time_entry), "");
1269 return;
1272 /* TODO: should handle other display modes as well... */
1274 if ((priv->display_mode & EGG_DATETIME_DISPLAY_SECOND) ||
1275 (priv->display_mode & EGG_DATETIME_DISPLAY_SECOND_OPT))
1276 s = get_time_string (priv->hour, priv->minute, priv->second);
1277 else {
1278 if(priv->hour == -1 && priv->minute == 0)
1279 s = g_strdup(_("no end time"));
1280 else s = get_time_string (priv->hour, priv->minute, 0xff);
1282 gtk_entry_set_text (GTK_ENTRY (priv->time_entry), s);
1284 g_free (s);
1287 /* Parse the current date entry and normalize the date. */
1288 static void
1289 parse_date (EggDateTime *edt)
1291 GDate date;
1292 g_date_set_parse (&date, gtk_entry_get_text (GTK_ENTRY (edt->priv->date_entry)));
1293 if (!g_date_valid (&date)) {
1294 edt->priv->no_date = TRUE;
1295 gtk_entry_set_text(GTK_ENTRY(edt->priv->date_entry), _("No Date"));
1296 return;
1299 edt->priv->year = date.year;
1300 edt->priv->month = date.month;
1301 edt->priv->day = date.day;
1302 edt->priv->date_valid = TRUE;
1304 normalize_date (edt);
1307 /* Parse the current time entry and normalize the time. */
1308 static void
1309 parse_time (EggDateTime *edt)
1311 const gchar *s;
1312 gchar *scp;
1313 unsigned int hour, minute, second = 0;
1314 size_t len1, len2, len3;
1316 /* Retrieve and Parse String */
1318 s = gtk_entry_get_text (GTK_ENTRY (edt->priv->time_entry));
1320 /* Translators: This is hh:mm:ss AM/PM. */
1321 if (sscanf (s, _("%u:%u:%u"), &hour, &minute, &second) < 2) {
1322 if (sscanf (s, "%u:%u:%u", &hour, &minute, &second) < 2) {
1323 if (edt->priv->lazy)
1324 edt->priv->time_valid = FALSE;
1325 return;
1329 if (hour > 23 || minute > 59 || second > 59) {
1330 if (edt->priv->lazy)
1331 edt->priv->time_valid = FALSE;
1332 return;
1335 /* AM/PM Handling */
1337 scp = g_strdup (s);
1338 scp = g_strchomp (scp);
1340 len1 = strlen (_("AM"));
1341 len2 = strlen (_("PM"));
1342 len3 = strlen (scp);
1344 if (len1 < len3 && !strcasecmp (scp + len3 - len1, _("AM"))) {
1345 if (hour == 12)
1346 hour = 0;
1348 if (len2 < len3 && !strcasecmp (scp + len3 - len2, _("PM"))) {
1349 if (hour == 12)
1350 hour = 0;
1351 hour += 12;
1354 /* Store Time */
1356 edt->priv->hour = hour;
1357 edt->priv->minute = minute;
1358 edt->priv->second = second;
1359 edt->priv->time_valid = TRUE;
1361 /* Cleanup */
1363 g_free (scp);
1365 normalize_time (edt);
1368 /* Clamp the current date to the date clamp range if lazy is turned off. */
1369 static void
1370 normalize_date (EggDateTime *edt)
1372 GDate date, min_date, max_date;
1374 if (edt->priv->lazy)
1375 return;
1377 g_date_clear (&date, 1);
1378 g_date_set_dmy (&date, edt->priv->day, edt->priv->month, edt->priv->year);
1379 g_date_clear (&min_date, 1);
1380 g_date_set_dmy (&min_date, edt->priv->clamp_minday, edt->priv->clamp_minmonth, edt->priv->clamp_minyear);
1381 g_date_clear (&max_date, 1);
1382 g_date_set_dmy (&max_date, edt->priv->clamp_maxday, edt->priv->clamp_maxmonth, edt->priv->clamp_maxyear);
1384 g_date_clamp (&date, &min_date, &max_date);
1386 edt->priv->year = date.year;
1387 edt->priv->month = date.month;
1388 edt->priv->day = date.day;
1389 edt->priv->date_valid = TRUE;
1392 /* Clamp the current time to the time clamp range if lazy is turned off. */
1393 static void
1394 normalize_time (EggDateTime *edt)
1396 if (edt->priv->lazy)
1397 return;
1399 if (edt->priv->hour < edt->priv->clamp_minhour) {
1400 edt->priv->hour = -1;;
1401 edt->priv->minute = edt->priv->clamp_minminute;
1402 edt->priv->second = edt->priv->clamp_minsecond;
1403 } else if (edt->priv->hour == edt->priv->clamp_minhour) {
1404 if (edt->priv->minute < edt->priv->clamp_minminute) {
1405 edt->priv->minute = edt->priv->clamp_minminute;
1406 edt->priv->second = edt->priv->clamp_minsecond;
1407 } else if (edt->priv->minute == edt->priv->clamp_minminute) {
1408 if (edt->priv->second < edt->priv->clamp_minsecond)
1409 edt->priv->second = edt->priv->clamp_minsecond;
1413 if (edt->priv->hour > edt->priv->clamp_maxhour) {
1414 edt->priv->hour = edt->priv->clamp_maxhour;
1415 edt->priv->minute = edt->priv->clamp_maxminute;
1416 edt->priv->second = edt->priv->clamp_maxsecond;
1417 } else if (edt->priv->hour == edt->priv->clamp_maxhour) {
1418 if (edt->priv->minute > edt->priv->clamp_maxminute) {
1419 edt->priv->minute = edt->priv->clamp_maxminute;
1420 edt->priv->second = edt->priv->clamp_maxsecond;
1421 } else if (edt->priv->minute == edt->priv->clamp_maxminute) {
1422 if (edt->priv->second > edt->priv->clamp_maxsecond)
1423 edt->priv->second = edt->priv->clamp_maxsecond;
1427 edt->priv->time_valid = TRUE;
1430 static void
1431 parse_and_update_date (EggDateTime *edt)
1433 if (edt->priv->lazy)
1434 return;
1436 parse_date (edt);
1437 update_date_label (edt);
1440 static void
1441 parse_and_update_time (EggDateTime *edt)
1443 if (edt->priv->lazy)
1444 return;
1446 parse_time (edt);
1447 update_time_label (edt);
1451 * Public Methods
1455 * egg_datetime_new:
1457 * Creates a new #EggDateTime widget. By default this widget will show
1458 * only the date component and is set to the current date and time.
1460 * Return value: a newly created #EggDateTime widget
1462 GtkWidget *
1463 egg_datetime_new (void)
1465 EggDateTime *edt;
1467 edt = g_object_new (EGG_TYPE_DATETIME, NULL);
1468 egg_datetime_set_from_time_t (edt, time (NULL));
1470 return GTK_WIDGET (edt);
1474 * egg_datetime_new_from_time_t:
1475 * @t: initial time and date
1477 * Creates a new #EggDateTime widget and sets it to the date and time
1478 * given as @t argument. This does also call egg_datetime_set_clamp_time_t().
1479 * By default this widget will show only the date component.
1481 * Return value: a newly created #EggDateTime widget
1483 GtkWidget *
1484 egg_datetime_new_from_time_t (time_t t)
1486 EggDateTime *edt;
1488 g_return_val_if_fail (t >= 0, NULL);
1490 edt = g_object_new (EGG_TYPE_DATETIME, NULL);
1491 egg_datetime_set_from_time_t (edt, t);
1492 egg_datetime_set_clamp_time_t (edt);
1494 return GTK_WIDGET (edt);
1498 * egg_datetime_new_from_struct_tm:
1499 * @tm: initial time and date
1501 * Creates a new #EggDateTime widget and sets it to the date and time
1502 * given as @tm argument. By default this widget will show only the date
1503 * component.
1505 * Return value: a newly created #EggDateTime widget
1507 GtkWidget *
1508 egg_datetime_new_from_struct_tm (struct tm *tm)
1510 EggDateTime *edt;
1512 g_return_val_if_fail (tm != NULL, NULL);
1514 edt = g_object_new (EGG_TYPE_DATETIME, NULL);
1515 egg_datetime_set_from_struct_tm (edt, tm);
1517 return GTK_WIDGET (edt);
1521 * egg_datetime_new_from_gdate:
1522 * @date: initial time and date
1524 * Creates a new #EggDateTime widget and sets it to the date and time
1525 * given as @date argument. By default this widget will show only the
1526 * date component.
1528 * Return value: a newly created #EggDateTime widget
1530 GtkWidget *
1531 egg_datetime_new_from_gdate (GDate *date)
1533 EggDateTime *edt;
1535 g_return_val_if_fail (date != NULL, NULL);
1537 edt = g_object_new (EGG_TYPE_DATETIME, NULL);
1538 egg_datetime_set_from_gdate (edt, date);
1540 return GTK_WIDGET (edt);
1544 * egg_datetime_new_from_datetime:
1545 * @year: initial year
1546 * @month: initial month
1547 * @day: initial day
1548 * @hour: initial hour
1549 * @minute: initial minute
1550 * @second: initial second
1552 * Creates a new #EggDateTime widget and sets it to the date and time
1553 * given as arguments.
1555 * Return value: a newly created #EggDateTime widget
1557 GtkWidget *
1558 egg_datetime_new_from_datetime (GDateYear year, GDateMonth month, GDateDay day, guint8 hour, guint8 minute, guint8 second)
1560 EggDateTime *edt;
1562 edt = g_object_new (EGG_TYPE_DATETIME, NULL);
1563 egg_datetime_set_date (edt, year, month, day);
1564 egg_datetime_set_time (edt, hour, minute, second);
1566 return GTK_WIDGET (edt);
1570 * egg_datetime_set_none:
1571 * @edt: an #EggDateTime
1573 * Sets the date to an invalid value. If lazy mode is turned off the date
1574 * and time will be set to a random value.
1576 void
1577 egg_datetime_set_none (EggDateTime *edt)
1579 g_return_if_fail (edt != NULL);
1580 g_return_if_fail (EGG_IS_DATETIME (edt));
1582 edt->priv->date_valid = FALSE;
1583 edt->priv->time_valid = FALSE;
1585 update_date_label (edt);
1586 update_time_label (edt);
1588 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
1589 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_TIME_CHANGED], 0);
1593 * egg_datetime_set_from_time_t:
1594 * @edt: an #EggDateTime
1595 * @t: date and time to set the widget to
1597 * Sets the date and time of the widget to @t.
1599 void
1600 egg_datetime_set_from_time_t (EggDateTime *edt, time_t t)
1602 struct tm tm;
1604 g_return_if_fail (edt != NULL);
1605 g_return_if_fail (EGG_IS_DATETIME (edt));
1607 if (localtime_r (&t, &tm)) {
1608 egg_datetime_set_date (edt, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
1609 egg_datetime_set_time (edt, tm.tm_hour, tm.tm_min, tm.tm_sec);
1610 } else
1611 egg_datetime_set_none (edt);
1615 * egg_datetime_get_as_time_t:
1616 * @edt: an #EggDateTime
1617 * @t: pointer to a %time_t value
1619 * Returns the current time as a %time_t value. If the currently entered
1620 * value is invalid and lazy mode is turned on or if the entered date
1621 * can't be represented as a %time_t value, the value is set to -1 and
1622 * FALSE is returned.
1624 * Return value: success indicator
1626 gboolean
1627 egg_datetime_get_as_time_t (EggDateTime *edt, time_t *t)
1629 struct tm tm;
1630 GDateYear year;
1631 GDateMonth month;
1632 GDateDay day;
1633 gint hour, minute, second;
1635 g_return_val_if_fail (edt != NULL, FALSE);
1636 g_return_val_if_fail (EGG_IS_DATETIME (edt), FALSE);
1638 if (!t)
1639 return FALSE;
1641 if (!egg_datetime_get_date (edt, &year, &month, &day)) {
1642 *t = -1;
1643 return FALSE;
1645 if (!egg_datetime_get_time (edt, &hour, &minute, &second)) {
1646 *t = -1;
1647 return FALSE;
1650 memset (&tm, 0, sizeof (struct tm));
1651 tm.tm_year = year - 1900;
1652 tm.tm_mon = month - 1;
1653 tm.tm_mday = day;
1654 tm.tm_hour = hour;
1655 tm.tm_min = minute;
1656 tm.tm_sec = second;
1658 *t = mktime (&tm);
1660 if (*t < 0) {
1661 *t = -1;
1662 return FALSE;
1665 return TRUE;
1669 * egg_datetime_set_from_struct_tm:
1670 * @edt: an #EggDateTime
1671 * @tm: date and time to set the widget to
1673 * Sets the date and time of the widget to @tm.
1675 void
1676 egg_datetime_set_from_struct_tm (EggDateTime *edt, struct tm *tm)
1678 g_return_if_fail (edt != NULL);
1679 g_return_if_fail (EGG_IS_DATETIME (edt));
1680 g_return_if_fail (tm != NULL);
1682 egg_datetime_set_date (edt, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
1683 egg_datetime_set_time (edt, tm->tm_hour, tm->tm_min, tm->tm_sec);
1687 * egg_datetime_get_as_struct_tm:
1688 * @edt: an #EggDateTime
1689 * @tm: pointer to an allocated struct tm
1691 * Fill the supplied struct tm with the widget's current date and time.
1692 * If the currently entered value is invalid and lazy mode is turned
1693 * on or if the entered date can't be represented as a struct tm, the
1694 * struct is filled with invalid data and FALSE is returned.
1696 * Return value: success indicator
1698 gboolean
1699 egg_datetime_get_as_struct_tm (EggDateTime *edt, struct tm *tm)
1701 GDateYear year;
1702 GDateMonth month;
1703 GDateDay day;
1704 gint hour, minute, second;
1706 g_return_val_if_fail (edt != NULL, FALSE);
1707 g_return_val_if_fail (EGG_IS_DATETIME (edt), FALSE);
1709 if (!tm)
1710 return FALSE;
1712 memset (tm, 0, sizeof (struct tm));
1714 if (!egg_datetime_get_date (edt, &year, &month, &day))
1715 return FALSE;
1716 if (!egg_datetime_get_time (edt, &hour, &minute, &second))
1717 return FALSE;
1719 tm->tm_year = year - 1900;
1720 tm->tm_mon = month - 1;
1721 tm->tm_mday = day;
1722 tm->tm_hour = hour;
1723 tm->tm_min = minute;
1724 tm->tm_sec = second;
1726 mktime (tm);
1728 return TRUE;
1732 * egg_datetime_set_from_gdate:
1733 * @edt: an #EggDateTime
1734 * @date: date to set the widget to
1736 * Sets the date of the widget to @date. The time will remain unchanged.
1738 void
1739 egg_datetime_set_from_gdate (EggDateTime *edt, GDate *date)
1741 GDateYear year;
1742 GDateMonth month;
1743 GDateDay day;
1745 g_return_if_fail (edt != NULL);
1746 g_return_if_fail (EGG_IS_DATETIME (edt));
1747 g_return_if_fail (date != NULL);
1749 year = g_date_get_year(date);
1750 month = g_date_get_month(date);
1751 day = g_date_get_day(date);
1752 g_return_if_fail(g_date_valid_dmy(day, month, year));
1754 if (g_date_valid (date))
1755 egg_datetime_set_date (edt, year, month, day);
1756 else
1757 egg_datetime_set_none (edt);
1761 * egg_datetime_get_as_gdate:
1762 * @edt: an #EggDateTime
1763 * @date: pointer to an allocated #GDate
1765 * Fills the supplied #GDate with the widget's current date. If the
1766 * currently entered date value is invalid and lazy mode is turned
1767 * on or if the entered date can't be represented as a #GDate, the
1768 * @date is set to an invalid value and FALSE is returned.
1770 * Return value: success indicator
1772 gboolean
1773 egg_datetime_get_as_gdate (EggDateTime *edt, GDate *date)
1775 GDateYear year;
1776 GDateMonth month;
1777 GDateDay day;
1779 g_return_val_if_fail (edt != NULL, FALSE);
1780 g_return_val_if_fail (EGG_IS_DATETIME (edt), FALSE);
1782 if (!date)
1783 return FALSE;
1785 g_date_clear (date, 1);
1787 if (!egg_datetime_get_date (edt, &year, &month, &day))
1788 return FALSE;
1790 g_date_set_dmy (date, day, month, year);
1792 return TRUE;
1796 * egg_datetime_set_date:
1797 * @edt: an #EggDateTime
1798 * @year: a #guint16 between 1 and 9999
1799 * @month: a #guint8 between 1 and 12
1800 * @day: a #guint8 between 1 and 28-31 (depending on @month)
1802 * Sets the date of the widget. The time will remain unchanged.
1804 void
1805 egg_datetime_set_date (EggDateTime *edt, GDateYear year, GDateMonth month, GDateDay day)
1807 g_return_if_fail (edt != NULL);
1808 g_return_if_fail (EGG_IS_DATETIME (edt));
1809 g_return_if_fail (year >= 1 && year <= 9999);
1810 g_return_if_fail (month >= 1 && month <= 12);
1811 g_return_if_fail (day >= 1 && day <= g_date_get_days_in_month (month, year));
1813 edt->priv->year = year;
1814 edt->priv->month = month;
1815 edt->priv->day = day;
1816 edt->priv->date_valid = TRUE;
1817 gtk_calendar_select_month(GTK_CALENDAR(edt->priv->calendar), month-1, year);
1818 gtk_calendar_select_day(GTK_CALENDAR(edt->priv->calendar), day);
1820 normalize_date (edt);
1821 update_date_label (edt);
1823 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
1827 * egg_datetime_get_date:
1828 * @edt: an #EggDateTime
1829 * @year: a pointer to a #guint16 or %NULL
1830 * @month: a pointer to #guint8 or %NULL
1831 * @day: a pointer to a #guint8 or %NULL
1833 * Fills the supplied arguments with the widget's current date. If the
1834 * currently entered date value is invalid and lazy mode is turned
1835 * on, the arguments are set to %EGG_DATETIME_INVALID_DATE and FALSE
1836 * is returned.
1838 * Return value: success indicator
1840 gboolean
1841 egg_datetime_get_date (EggDateTime *edt, GDateYear *year, GDateMonth *month, GDateDay *day)
1843 g_return_val_if_fail (edt != NULL, FALSE);
1844 g_return_val_if_fail (EGG_IS_DATETIME (edt), FALSE);
1846 parse_date (edt);
1848 if (!edt->priv->date_valid) {
1849 if (year)
1850 *year = 0;
1851 if (month)
1852 *month = 0;
1853 if (day)
1854 *day = 0;
1855 return FALSE;
1858 if (year)
1859 *year = edt->priv->year;
1860 if (month)
1861 *month = edt->priv->month;
1862 if (day)
1863 *day = edt->priv->day;
1865 return TRUE;
1869 * egg_datetime_set_time:
1870 * @edt: an #EggDateTime
1871 * @hour: a #guint8 between 0 and 23
1872 * @minute: a #guint8 between 0 and 59
1873 * @second: a #guint8 between 0 and 59
1875 * Sets the time of the widget. The date will remain unchanged.
1877 void
1878 egg_datetime_set_time (EggDateTime *edt, gint hour, gint minute, guint8 second)
1880 g_return_if_fail (edt != NULL);
1881 g_return_if_fail (EGG_IS_DATETIME (edt));
1882 g_return_if_fail (hour <= 23);
1883 g_return_if_fail (minute <= 59);
1884 g_return_if_fail (second <= 59);
1886 edt->priv->hour = hour;
1887 edt->priv->minute = minute;
1888 edt->priv->second = second;
1889 edt->priv->time_valid = TRUE;
1891 normalize_time (edt);
1892 update_time_label (edt);
1894 timelist_set_time(edt->priv->timelist, hour, minute);
1896 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_TIME_CHANGED], 0);
1900 * egg_datetime_get_date:
1901 * @edt: an #EggDateTime
1902 * @hour: a pointer to a #guint8 or %NULL
1903 * @minute: a pointer to #guint8 or %NULL
1904 * @second: a pointer to a #guint8 or %NULL
1906 * Fills the supplied arguments with the widget's current time. If the
1907 * currently entered time value is invalid and lazy mode is turned
1908 * on, the arguments are set to %EGG_DATETIME_INVALID_TIME and FALSE
1909 * is returned.
1911 * Return value: success indicator
1913 gboolean
1914 egg_datetime_get_time (EggDateTime *edt, gint *hour, gint *minute, gint *second)
1916 g_return_val_if_fail (edt != NULL, FALSE);
1917 g_return_val_if_fail (EGG_IS_DATETIME (edt), FALSE);
1919 parse_time (edt);
1921 if (!edt->priv->time_valid) {
1922 if (hour)
1923 *hour = 0xff;
1924 if (minute)
1925 *minute = 0xff;
1926 if (second)
1927 *second = 0xff;
1928 return FALSE;
1931 if (hour)
1932 *hour = edt->priv->hour;
1933 if (minute)
1934 *minute = edt->priv->minute;
1935 if (second)
1936 *second = edt->priv->second;
1938 return TRUE;
1942 * egg_datetime_set_lazy:
1943 * @edt: an #EggDateTime
1944 * @lazy: a boolean value
1946 * Turns the widget's lazy mode on or off. In lazy mode the widget will
1947 * allow invalid values to be entered. If lazy mode is turned off the
1948 * widget will normalize all invalid values entered in the date and time
1949 * widgets to the nearest valid value. This guarantees that the get methods
1950 * will always return valid values.
1952 * Lazy mode defaults to %TRUE.
1954 void
1955 egg_datetime_set_lazy (EggDateTime *edt, gboolean lazy)
1957 g_return_if_fail (edt != NULL);
1958 g_return_if_fail (EGG_IS_DATETIME (edt));
1960 edt->priv->lazy = lazy ? TRUE : FALSE;
1962 parse_and_update_date (edt);
1963 parse_and_update_time (edt);
1965 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
1966 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_TIME_CHANGED], 0);
1970 * egg_datetime_get_lazy:
1971 * @edt: an #EggDateTime
1973 * Returns whether the widget is in lazy mode.
1975 * Return value: a boolean value
1977 gboolean
1978 egg_datetime_get_lazy (EggDateTime *edt)
1980 g_return_val_if_fail (edt != NULL, FALSE);
1981 g_return_val_if_fail (EGG_IS_DATETIME (edt), FALSE);
1983 return edt->priv->lazy;
1987 * egg_datetime_set_display_mode:
1988 * @edt: an #EggDateTime
1989 * @mode: new display mode
1991 * Sets the widget's new display mode to @mode. The display mode defaults
1992 * to %EGG_DATETIME_DISPLAY_DATE.
1994 void
1995 egg_datetime_set_display_mode (EggDateTime *edt, EggDateTimeDisplayMode mode)
1997 g_return_if_fail (edt != NULL);
1998 g_return_if_fail (EGG_IS_DATETIME (edt));
2000 edt->priv->display_mode = mode;
2002 apply_display_mode (edt);
2006 * egg_datetime_get_display_mode:
2007 * @edt: an #EggDateTime
2009 * Returns the current display mode.
2011 * Return value: The current display mode.
2013 EggDateTimeDisplayMode
2014 egg_datetime_get_display_mode (EggDateTime *edt)
2016 g_return_val_if_fail (edt != NULL, 0);
2017 g_return_val_if_fail (EGG_IS_DATETIME (edt), 0);
2019 return edt->priv->display_mode;
2023 * egg_datetime_set_clamp_date:
2024 * @edt: an #EggDateTime
2025 * @minyear: minimum year
2026 * @minmonth: minimum month
2027 * @minday: minimum day
2028 * @maxyear: maximum year
2029 * @maxmonth: maximum month
2030 * @maxday: maximum day
2032 * Limits the allowed dates to the range given. If lazy mode is
2033 * turned off, dates that are outside of this range are snapped to the
2034 * minimum or maximum date. Otherwise such dates return an invalid value.
2036 * This defaults to the minimum date 1-1-1 and maximum date 9999-12-31.
2037 * The maximum year is always limited to 9999.
2039 void
2040 egg_datetime_set_clamp_date (EggDateTime *edt,
2041 GDateYear minyear,
2042 GDateMonth minmonth,
2043 GDateDay minday,
2044 GDateYear maxyear,
2045 GDateMonth maxmonth,
2046 GDateDay maxday)
2048 if (maxyear > 9999)
2049 maxyear = 9999;
2051 g_return_if_fail (minyear >= 1 && minyear <= 9999 && maxyear >= 1);
2052 g_return_if_fail (minmonth >= 1 && minmonth <= 12 && maxmonth >= 1 && maxmonth <= 12);
2053 g_return_if_fail (minday >= 1 && minday <= g_date_get_days_in_month (minmonth, minyear));
2054 g_return_if_fail (maxday >= 1 && maxday <= g_date_get_days_in_month (maxmonth, maxyear));
2055 g_return_if_fail (minyear <= maxyear);
2056 g_return_if_fail (minyear < maxyear || minmonth <= maxmonth);
2057 g_return_if_fail (minyear < maxyear || minmonth < maxmonth || minday <= maxday);
2059 edt->priv->clamp_minyear = minyear;
2060 edt->priv->clamp_minmonth = minmonth;
2061 edt->priv->clamp_minday = minday;
2062 edt->priv->clamp_maxyear = maxyear;
2063 edt->priv->clamp_maxmonth = maxmonth;
2064 edt->priv->clamp_maxday = maxday;
2066 parse_and_update_date (edt);
2068 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_DATE_CHANGED], 0);
2072 * egg_datetime_set_clamp_time:
2073 * @edt: an #EggDateTime
2074 * @minhour: minimum hour
2075 * @minminute: minimum minute
2076 * @minsecond: minimum second
2077 * @maxhour: maximum hour
2078 * @maxminute: maximum minute
2079 * @maxsecond: maximum second
2081 * Limits the allowed times to the range given. If lazy mode is turned
2082 * off, times that are outside of this range are snapped to the minimum or
2083 * maximum time. Otherwise such times return an invalid value.
2085 void
2086 egg_datetime_set_clamp_time (EggDateTime *edt, guint8 minhour, guint8 minminute, guint8 minsecond, guint8 maxhour, guint8 maxminute, guint8 maxsecond)
2088 g_return_if_fail (minhour <= 23 && maxhour <= 23);
2089 g_return_if_fail (minminute <= 59 && maxminute <= 59);
2090 g_return_if_fail (minsecond <= 59 && maxsecond <= 59);
2091 g_return_if_fail (minhour <= maxhour);
2092 g_return_if_fail (minhour < maxhour || minminute <= maxminute);
2093 g_return_if_fail (minhour < maxhour || minminute < maxminute || minsecond <= maxsecond);
2095 edt->priv->clamp_minhour = minhour;
2096 edt->priv->clamp_minminute = minminute;
2097 edt->priv->clamp_minsecond = minsecond;
2098 edt->priv->clamp_maxhour = maxhour;
2099 edt->priv->clamp_maxminute = maxminute;
2100 edt->priv->clamp_maxsecond = maxsecond;
2102 clamp_time_labels (edt);
2103 parse_and_update_time (edt);
2105 g_signal_emit (G_OBJECT (edt), egg_datetime_signals[SIGNAL_TIME_CHANGED], 0);
2109 * egg_datetime_set_clamp_time_t:
2110 * @edt: an #EggDateTime
2112 * Clamps the allowed dates of the widget to valid #time_t values.
2113 * The time clamp settings are not changed.
2115 void
2116 egg_datetime_set_clamp_time_t (EggDateTime *edt)
2118 time_t t;
2119 struct tm start_tm, end_tm;
2120 guint bits;
2121 guint16 year;
2122 guint8 month, day;
2124 g_return_if_fail (edt != NULL);
2125 g_return_if_fail (EGG_IS_DATETIME (edt));
2127 t = 0;
2128 gmtime_r (&t, &start_tm);
2130 /* evil hack */
2131 bits = time_t_bits ();
2132 t = ~0;
2133 t &= ~(1 << (bits - 1));
2135 gmtime_r (&t, &end_tm);
2137 /* Subtract one day from the end date, since not all times of
2138 * the last day can be represented.
2141 year = end_tm.tm_year + 1900;
2142 month = end_tm.tm_mon + 1;
2143 day = end_tm.tm_mday;
2145 if (--day == 0) {
2146 if (--month == 0) {
2147 year--;
2148 month = 12;
2151 day = g_date_get_days_in_month (month, year);
2154 egg_datetime_set_clamp_date (edt, start_tm.tm_year + 1900, start_tm.tm_mon + 1, start_tm.tm_mday, year, month, day);
2158 * egg_datetime_get_clamp_date:
2159 * @edt: an #EggDateTime
2160 * @minyear: #guint16 pointer or %NULL
2161 * @minmonth: #guint8 pointer or %NULL
2162 * @minday: #guint8 pointer or %NULL
2163 * @maxyear: #guint16 pointer or %NULL
2164 * @maxmonth: #guint8 pointer or %NULL
2165 * @maxday: #guint8 pointer or %NULL
2167 * Returns the current date limit settings.
2169 void
2170 egg_datetime_get_clamp_date (EggDateTime *edt,
2171 GDateYear *minyear,
2172 GDateMonth *minmonth,
2173 GDateDay *minday,
2174 GDateYear *maxyear,
2175 GDateMonth *maxmonth,
2176 GDateDay *maxday)
2178 g_return_if_fail (edt != NULL);
2179 g_return_if_fail (EGG_IS_DATETIME (edt));
2181 if (minyear)
2182 *minyear = edt->priv->clamp_minyear;
2183 if (minmonth)
2184 *minmonth = edt->priv->clamp_minmonth;
2185 if (minday)
2186 *minday = edt->priv->clamp_minday;
2187 if (maxyear)
2188 *maxyear = edt->priv->clamp_maxyear;
2189 if (maxmonth)
2190 *maxmonth = edt->priv->clamp_maxmonth;
2191 if (maxday)
2192 *maxday = edt->priv->clamp_maxday;
2196 * egg_datetime_get_clamp_time:
2197 * @edt: an #EggDateTime
2198 * @minhour: #guint8 pointer or %NULL
2199 * @minminute: #guint8 pointer or %NULL
2200 * @minsecond: #guint8 pointer or %NULL
2201 * @maxhour: #guint8 pointer or %NULL
2202 * @maxminute: #guint8 pointer or %NULL
2203 * @maxsecond: #guint8 pointer or %NULL
2205 * Returns the current time limit settings.
2207 void
2208 egg_datetime_get_clamp_time (EggDateTime *edt, guint8 *minhour, guint8 *minminute, guint8 *minsecond, guint8 *maxhour, guint8 *maxminute, guint8 *maxsecond)
2210 g_return_if_fail (edt != NULL);
2211 g_return_if_fail (EGG_IS_DATETIME (edt));
2213 if (minhour)
2214 *minhour = edt->priv->clamp_minhour;
2215 if (minminute)
2216 *minminute = edt->priv->clamp_minminute;
2217 if (minsecond)
2218 *minsecond = edt->priv->clamp_minsecond;
2219 if (maxhour)
2220 *maxhour = edt->priv->clamp_maxhour;
2221 if (maxminute)
2222 *maxminute = edt->priv->clamp_maxminute;
2223 if (maxsecond)
2224 *maxsecond = edt->priv->clamp_maxsecond;
2228 * egg_datetime_get_date_layout:
2229 * @edt: an #EggDateTime
2231 * Gets the PangoLayout used to display the date. See gtk_entry_get_layout()
2232 * for more information. The returned layout is owned by the date/time
2233 * widget so need not be freed by the caller.
2235 * Return value: the #PangoLayout for this widget's date part
2237 PangoLayout *
2238 egg_datetime_get_date_layout (EggDateTime *edt)
2240 g_return_val_if_fail (edt != NULL, NULL);
2241 g_return_val_if_fail (EGG_IS_DATETIME (edt), NULL);
2243 return gtk_entry_get_layout (GTK_ENTRY (edt->priv->date_entry));
2247 * egg_datetime_get_time_layout:
2248 * @edt: an #EggDateTime
2250 * Gets the PangoLayout used to display the time. See gtk_entry_get_layout()
2251 * for more information. The returned layout is owned by the date/time
2252 * widget so need not be freed by the caller.
2254 * Return value: the #PangoLayout for this widget's time part
2256 PangoLayout *
2257 egg_datetime_get_time_layout (EggDateTime *edt)
2259 g_return_val_if_fail (edt != NULL, NULL);
2260 g_return_val_if_fail (EGG_IS_DATETIME (edt), NULL);
2262 return gtk_entry_get_layout (GTK_ENTRY (edt->priv->time_entry));
2265 /**************************************************************************/
2267 /* This is a private time list widget implementation for use as time popup.
2270 static GtkWidget *
2271 timelist_new (EggDateTime *edt)
2273 GtkWidget *timelist;
2274 GtkWidget *list;
2275 GtkListStore *model;
2276 GtkTreeSelection *selection;
2277 GtkCellRenderer *renderer;
2279 timelist = gtk_scrolled_window_new (NULL, NULL);
2280 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (timelist),
2281 GTK_POLICY_NEVER,
2282 GTK_POLICY_ALWAYS);
2284 model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_UINT);
2285 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
2286 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
2287 gtk_widget_show (list);
2289 renderer = gtk_cell_renderer_text_new ();
2290 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (list),
2292 _("Time"),
2293 renderer,
2294 "text", 0,
2295 NULL);
2297 gtk_container_add (GTK_CONTAINER (timelist), list);
2299 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
2300 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
2301 g_signal_connect_swapped(G_OBJECT(list), "row-activated", G_CALLBACK(time_popup_hide), edt);
2302 timelist_set_list (TIMELIST (timelist), 00, 00, 23, 59);
2304 return timelist;
2307 static void
2308 timelist_set_list (Timelist *timelist,
2309 guint8 minhour, guint8 minminute,
2310 guint8 maxhour, guint8 maxminute)
2312 GtkWidget *tree = gtk_bin_get_child (GTK_BIN (timelist));
2313 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
2314 gint minidx, maxidx;
2315 GtkTreeIter iter;
2316 gint i;
2318 minidx = minhour * 2 + (minminute + 29) / 30;
2319 maxidx = maxhour * 2 + (maxminute + 29) / 30;
2321 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
2322 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2323 0, _("no end time"),
2324 1, -1,
2325 -1);
2327 for (i = minidx; i < maxidx; i++) {
2328 gchar *s;
2329 guint hour, minute;
2331 hour = i / 2;
2332 minute = (i % 2) * 30;
2334 s = get_time_string (hour, minute, 0xff);
2336 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
2337 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2338 0, s,
2339 1, hour * 100 + minute,
2340 -1);
2342 g_free (s);
2346 static void
2347 timelist_set_time (Timelist *timelist, gint hour, gint minute)
2349 GtkWidget *tree = gtk_bin_get_child (GTK_BIN (timelist));
2350 GtkTreeModel *model;
2351 GtkTreeSelection *selection;
2352 GtkTreeIter iter;
2354 model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
2355 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
2357 if (!gtk_tree_model_get_iter_first (model, &iter))
2359 return;
2362 do {
2363 guint time;
2364 gtk_tree_model_get (model, &iter, 1, &time, -1);
2366 if (time / 100 == hour && time % 100 == minute) {
2367 gtk_tree_selection_select_iter (selection, &iter);
2368 return;
2371 } while (gtk_tree_model_iter_next (model, &iter));
2373 gtk_tree_selection_unselect_all (selection);
2376 static gboolean
2377 timelist_get_time (Timelist *timelist, gint *hour, gint *minute)
2379 GtkWidget *tree = gtk_bin_get_child (GTK_BIN (timelist));
2380 GtkTreeSelection *selection;
2381 GtkTreeModel *model;
2382 GtkTreeIter iter;
2383 guint time;
2385 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
2387 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
2389 return FALSE;
2392 gtk_tree_model_get (model, &iter, 1, &time, -1);
2394 if(time == -1)
2396 *hour = -1;
2397 *minute = 0;
2399 else
2401 if (hour)
2402 *hour = time / 100;
2403 if (minute)
2404 *minute = time % 100;
2406 return TRUE;
2409 static void
2410 timelist_clamp (Timelist *timelist,
2411 guint8 minhour, guint8 minminute,
2412 guint8 maxhour, guint8 maxminute)
2414 timelist_set_list (timelist, minhour, minminute, maxhour, maxminute);
2417 static void
2418 timelist_selection_cb (Timelist *timelist, GtkTreeSelection *selection)
2420 void (*cb)(gpointer, Timelist *);
2421 gpointer data;
2423 cb = g_object_get_data (G_OBJECT (selection), "cb");
2424 data = g_object_get_data (G_OBJECT (selection), "data");
2426 cb (data, timelist);
2429 static void
2430 timelist_set_selection_callback (Timelist *timelist, void (*cb)(void), gpointer data)
2432 GtkWidget *tree = gtk_bin_get_child (GTK_BIN (timelist));
2433 GtkTreeSelection *selection;
2435 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
2437 g_object_set_data (G_OBJECT (selection), "cb", cb);
2438 g_object_set_data (G_OBJECT (selection), "data", data);
2439 g_signal_connect_swapped (G_OBJECT (selection), "changed",
2440 G_CALLBACK (timelist_selection_cb), timelist);