evolution-3.6.4/widgets/misc/e-dateedit.c

No issues found

   1 /*
   2  * This library is free software; you can redistribute it and/or
   3  * modify it under the terms of the GNU Library General Public License as
   4  * published by the Free Software Foundation; either version 2 of the
   5  * License, or (at your option) any later version.
   6  *
   7  * This library is distributed in the hope that it will be useful,
   8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  10  * Lesser General Public License for more details.
  11  *
  12  * You should have received a copy of the GNU Library General Public
  13  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  14  *
  15  * Authors:
  16  *		Damon Chaplin <damon@ximian.com>
  17  *
  18  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  19  */
  20 
  21 /*
  22  * EDateEdit - a widget based on GnomeDateEdit to provide a date & optional
  23  * time field with popups for entering a date.
  24  */
  25 
  26 #ifdef HAVE_CONFIG_H
  27 #include <config.h>
  28 #endif
  29 
  30 #include "e-dateedit.h"
  31 
  32 #include <ctype.h>
  33 #include <stdio.h>
  34 #include <stdlib.h>
  35 #include <string.h>
  36 #include <gdk/gdkkeysyms.h>
  37 #include <atk/atkrelation.h>
  38 #include <atk/atkrelationset.h>
  39 #include <glib/gi18n.h>
  40 
  41 #include <libebackend/libebackend.h>
  42 
  43 #include <e-util/e-util.h>
  44 #include "e-calendar.h"
  45 
  46 #define E_DATE_EDIT_GET_PRIVATE(obj) \
  47 	(G_TYPE_INSTANCE_GET_PRIVATE \
  48 	((obj), E_TYPE_DATE_EDIT, EDateEditPrivate))
  49 
  50 struct _EDateEditPrivate {
  51 	GtkWidget *date_entry;
  52 	GtkWidget *date_button;
  53 
  54 	GtkWidget *space;
  55 
  56 	GtkWidget *time_combo;
  57 
  58 	GtkWidget *cal_popup;
  59 	GtkWidget *calendar;
  60 	GtkWidget *now_button;
  61 	GtkWidget *today_button;
  62 	GtkWidget *none_button;		/* This will only be visible if a
  63 					 * 'None' date/time is permitted. */
  64 
  65 	gboolean show_date;
  66 	gboolean show_time;
  67 	gboolean use_24_hour_format;
  68 
  69 	/* This is TRUE if we want to make the time field insensitive rather
  70 	 * than hide it when set_show_time() is called. */
  71 	gboolean make_time_insensitive;
  72 
  73 	/* This is the range of hours we show in the time popup. */
  74 	gint lower_hour;
  75 	gint upper_hour;
  76 
  77 	/* This indicates whether the last date committed was invalid.
  78 	 * (A date is committed by hitting Return, moving the keyboard focus,
  79 	 * or selecting a date in the popup). Note that this only indicates
  80 	 * that the date couldn't be parsed. A date set to 'None' is valid
  81 	 * here, though e_date_edit_date_is_valid() will return FALSE if an
  82 	 * empty date isn't actually permitted. */
  83 	gboolean date_is_valid;
  84 
  85 	/* This is the last valid date which was set. If the date was set to
  86 	 * 'None' or empty, date_set_to_none will be TRUE and the other fields
  87 	 * are undefined, so don't use them. */
  88 	gboolean date_set_to_none;
  89 	gint year;
  90 	gint month;
  91 	gint day;
  92 
  93 	/* This indicates whether the last time committed was invalid.
  94 	 * (A time is committed by hitting Return, moving the keyboard focus,
  95 	 * or selecting a time in the popup). Note that this only indicates
  96 	 * that the time couldn't be parsed. An empty/None time is valid
  97 	 * here, though e_date_edit_time_is_valid() will return FALSE if an
  98 	 * empty time isn't actually permitted. */
  99 	gboolean time_is_valid;
 100 
 101 	/* This is the last valid time which was set. If the time was set to
 102 	 * 'None' or empty, time_set_to_none will be TRUE and the other fields
 103 	 * are undefined, so don't use them. */
 104 	gboolean time_set_to_none;
 105 	gint hour;
 106 	gint minute;
 107 
 108 	EDateEditGetTimeCallback time_callback;
 109 	gpointer time_callback_data;
 110 	GDestroyNotify time_callback_destroy;
 111 
 112 	gboolean twodigit_year_can_future;
 113 
 114 	/* set to TRUE when the date has been changed by typing to the entry */
 115 	gboolean has_been_changed;
 116 
 117 	gboolean allow_no_date_set;
 118 };
 119 
 120 enum {
 121 	PROP_0,
 122 	PROP_ALLOW_NO_DATE_SET,
 123 	PROP_SHOW_DATE,
 124 	PROP_SHOW_TIME,
 125 	PROP_SHOW_WEEK_NUMBERS,
 126 	PROP_USE_24_HOUR_FORMAT,
 127 	PROP_WEEK_START_DAY,
 128 	PROP_TWODIGIT_YEAR_CAN_FUTURE,
 129 	PROP_SET_NONE
 130 };
 131 
 132 enum {
 133 	CHANGED,
 134 	LAST_SIGNAL
 135 };
 136 
 137 static void create_children			(EDateEdit	*dedit);
 138 static gboolean e_date_edit_mnemonic_activate	(GtkWidget	*widget,
 139 						 gboolean	 group_cycling);
 140 static void e_date_edit_grab_focus		(GtkWidget	*widget);
 141 
 142 static gint on_date_entry_key_press		(GtkWidget	*widget,
 143 						 GdkEventKey	*event,
 144 						 EDateEdit	*dedit);
 145 static gint on_date_entry_key_release		(GtkWidget	*widget,
 146 						 GdkEventKey	*event,
 147 						 EDateEdit	*dedit);
 148 static void on_date_button_clicked		(GtkWidget	*widget,
 149 						 EDateEdit	*dedit);
 150 static void e_date_edit_show_date_popup		(EDateEdit	*dedit);
 151 static void position_date_popup			(EDateEdit	*dedit);
 152 static void on_date_popup_none_button_clicked	(GtkWidget	*button,
 153 						 EDateEdit	*dedit);
 154 static void on_date_popup_today_button_clicked	(GtkWidget	*button,
 155 						 EDateEdit	*dedit);
 156 static void on_date_popup_now_button_clicked	(GtkWidget	*button,
 157 						 EDateEdit	*dedit);
 158 static gint on_date_popup_delete_event		(GtkWidget	*widget,
 159 						 EDateEdit	*dedit);
 160 static gint on_date_popup_key_press		(GtkWidget	*widget,
 161 						 GdkEventKey	*event,
 162 						 EDateEdit	*dedit);
 163 static gint on_date_popup_button_press		(GtkWidget	*widget,
 164 						 GdkEventButton *event,
 165 						 gpointer	 data);
 166 static void on_date_popup_date_selected		(ECalendarItem	*calitem,
 167 						 EDateEdit	*dedit);
 168 static void hide_date_popup			(EDateEdit	*dedit);
 169 static void rebuild_time_popup			(EDateEdit	*dedit);
 170 static gboolean field_set_to_none		(const gchar	*text);
 171 static gboolean e_date_edit_parse_date		(EDateEdit	*dedit,
 172 						 const gchar	*date_text,
 173 						 struct tm	*date_tm);
 174 static gboolean e_date_edit_parse_time		(EDateEdit	*dedit,
 175 						 const gchar	*time_text,
 176 						 struct tm	*time_tm);
 177 static void on_date_edit_time_selected		(GtkComboBox	*combo,
 178 						 EDateEdit	*dedit);
 179 static gint on_time_entry_key_press		(GtkWidget	*widget,
 180 						 GdkEventKey	*event,
 181 						 EDateEdit	*dedit);
 182 static gint on_time_entry_key_release		(GtkWidget	*widget,
 183 						 GdkEventKey	*event,
 184 						 EDateEdit	*dedit);
 185 static gint on_date_entry_focus_out		(GtkEntry	*entry,
 186 						 GdkEventFocus  *event,
 187 						 EDateEdit	*dedit);
 188 static gint on_time_entry_focus_out		(GtkEntry	*entry,
 189 						 GdkEventFocus  *event,
 190 						 EDateEdit	*dedit);
 191 static void e_date_edit_update_date_entry	(EDateEdit	*dedit);
 192 static void e_date_edit_update_time_entry	(EDateEdit	*dedit);
 193 static void e_date_edit_update_time_combo_state	(EDateEdit	*dedit);
 194 static void e_date_edit_check_date_changed	(EDateEdit	*dedit);
 195 static void e_date_edit_check_time_changed	(EDateEdit	*dedit);
 196 static gboolean e_date_edit_set_date_internal	(EDateEdit	*dedit,
 197 						 gboolean	 valid,
 198 						 gboolean	 none,
 199 						 gint		 year,
 200 						 gint		 month,
 201 						 gint		 day);
 202 static gboolean e_date_edit_set_time_internal	(EDateEdit	*dedit,
 203 						 gboolean	 valid,
 204 						 gboolean	 none,
 205 						 gint		 hour,
 206 						 gint		 minute);
 207 
 208 static gint signals[LAST_SIGNAL];
 209 
 210 G_DEFINE_TYPE_WITH_CODE (
 211 	EDateEdit,
 212 	e_date_edit,
 213 	GTK_TYPE_HBOX,
 214 	G_IMPLEMENT_INTERFACE (
 215 		E_TYPE_EXTENSIBLE, NULL))
 216 
 217 static void
 218 date_edit_set_property (GObject *object,
 219                         guint property_id,
 220                         const GValue *value,
 221                         GParamSpec *pspec)
 222 {
 223 	switch (property_id) {
 224 		case PROP_ALLOW_NO_DATE_SET:
 225 			e_date_edit_set_allow_no_date_set (
 226 				E_DATE_EDIT (object),
 227 				g_value_get_boolean (value));
 228 			return;
 229 
 230 		case PROP_SHOW_DATE:
 231 			e_date_edit_set_show_date (
 232 				E_DATE_EDIT (object),
 233 				g_value_get_boolean (value));
 234 			return;
 235 
 236 		case PROP_SHOW_TIME:
 237 			e_date_edit_set_show_time (
 238 				E_DATE_EDIT (object),
 239 				g_value_get_boolean (value));
 240 			return;
 241 
 242 		case PROP_SHOW_WEEK_NUMBERS:
 243 			e_date_edit_set_show_week_numbers (
 244 				E_DATE_EDIT (object),
 245 				g_value_get_boolean (value));
 246 			return;
 247 
 248 		case PROP_USE_24_HOUR_FORMAT:
 249 			e_date_edit_set_use_24_hour_format (
 250 				E_DATE_EDIT (object),
 251 				g_value_get_boolean (value));
 252 			return;
 253 
 254 		case PROP_WEEK_START_DAY:
 255 			e_date_edit_set_week_start_day (
 256 				E_DATE_EDIT (object),
 257 				g_value_get_int (value));
 258 			return;
 259 
 260 		case PROP_TWODIGIT_YEAR_CAN_FUTURE:
 261 			e_date_edit_set_twodigit_year_can_future (
 262 				E_DATE_EDIT (object),
 263 				g_value_get_boolean (value));
 264 			return;
 265 
 266 		case PROP_SET_NONE:
 267 			if (g_value_get_boolean (value))
 268 				e_date_edit_set_time (E_DATE_EDIT (object), -1);
 269 			return;
 270 	}
 271 
 272 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 273 }
 274 
 275 static void
 276 date_edit_get_property (GObject *object,
 277                         guint property_id,
 278                         GValue *value,
 279                         GParamSpec *pspec)
 280 {
 281 	switch (property_id) {
 282 		case PROP_ALLOW_NO_DATE_SET:
 283 			g_value_set_boolean (
 284 				value, e_date_edit_get_allow_no_date_set (
 285 				E_DATE_EDIT (object)));
 286 			return;
 287 
 288 		case PROP_SHOW_DATE:
 289 			g_value_set_boolean (
 290 				value, e_date_edit_get_show_date (
 291 				E_DATE_EDIT (object)));
 292 			return;
 293 
 294 		case PROP_SHOW_TIME:
 295 			g_value_set_boolean (
 296 				value, e_date_edit_get_show_time (
 297 				E_DATE_EDIT (object)));
 298 			return;
 299 
 300 		case PROP_SHOW_WEEK_NUMBERS:
 301 			g_value_set_boolean (
 302 				value, e_date_edit_get_show_week_numbers (
 303 				E_DATE_EDIT (object)));
 304 			return;
 305 
 306 		case PROP_USE_24_HOUR_FORMAT:
 307 			g_value_set_boolean (
 308 				value, e_date_edit_get_use_24_hour_format (
 309 				E_DATE_EDIT (object)));
 310 			return;
 311 
 312 		case PROP_WEEK_START_DAY:
 313 			g_value_set_int (
 314 				value, e_date_edit_get_week_start_day (
 315 				E_DATE_EDIT (object)));
 316 			return;
 317 
 318 		case PROP_TWODIGIT_YEAR_CAN_FUTURE:
 319 			g_value_set_boolean (
 320 				value, e_date_edit_get_twodigit_year_can_future (
 321 				E_DATE_EDIT (object)));
 322 			return;
 323 	}
 324 
 325 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 326 }
 327 
 328 static void
 329 date_edit_dispose (GObject *object)
 330 {
 331 	EDateEdit *dedit;
 332 
 333 	dedit = E_DATE_EDIT (object);
 334 
 335 	e_date_edit_set_get_time_callback (dedit, NULL, NULL, NULL);
 336 
 337 	if (dedit->priv->cal_popup != NULL) {
 338 		gtk_widget_destroy (dedit->priv->cal_popup);
 339 		dedit->priv->cal_popup = NULL;
 340 	}
 341 
 342 	/* Chain up to parent's dispose() method. */
 343 	G_OBJECT_CLASS (e_date_edit_parent_class)->dispose (object);
 344 }
 345 
 346 static void
 347 e_date_edit_class_init (EDateEditClass *class)
 348 {
 349 	GObjectClass *object_class;
 350 	GtkWidgetClass *widget_class;
 351 
 352 	g_type_class_add_private (class, sizeof (EDateEditPrivate));
 353 
 354 	object_class = G_OBJECT_CLASS (class);
 355 	object_class->set_property = date_edit_set_property;
 356 	object_class->get_property = date_edit_get_property;
 357 	object_class->dispose = date_edit_dispose;
 358 
 359 	widget_class = GTK_WIDGET_CLASS (class);
 360 	widget_class->mnemonic_activate = e_date_edit_mnemonic_activate;
 361 	widget_class->grab_focus = e_date_edit_grab_focus;
 362 
 363 	g_object_class_install_property (
 364 		object_class,
 365 		PROP_ALLOW_NO_DATE_SET,
 366 		g_param_spec_boolean (
 367 			"allow-no-date-set",
 368 			"Allow No Date Set",
 369 			NULL,
 370 			FALSE,
 371 			G_PARAM_READWRITE));
 372 
 373 	g_object_class_install_property (
 374 		object_class,
 375 		PROP_SHOW_DATE,
 376 		g_param_spec_boolean (
 377 			"show-date",
 378 			"Show Date",
 379 			NULL,
 380 			TRUE,
 381 			G_PARAM_READWRITE));
 382 
 383 	g_object_class_install_property (
 384 		object_class,
 385 		PROP_SHOW_TIME,
 386 		g_param_spec_boolean (
 387 			"show-time",
 388 			"Show Time",
 389 			NULL,
 390 			TRUE,
 391 			G_PARAM_READWRITE));
 392 
 393 	g_object_class_install_property (
 394 		object_class,
 395 		PROP_SHOW_WEEK_NUMBERS,
 396 		g_param_spec_boolean (
 397 			"show-week-numbers",
 398 			"Show Week Numbers",
 399 			NULL,
 400 			TRUE,
 401 			G_PARAM_READWRITE));
 402 
 403 	g_object_class_install_property (
 404 		object_class,
 405 		PROP_USE_24_HOUR_FORMAT,
 406 		g_param_spec_boolean (
 407 			"use-24-hour-format",
 408 			"Use 24-Hour Format",
 409 			NULL,
 410 			TRUE,
 411 			G_PARAM_READWRITE));
 412 
 413 	g_object_class_install_property (
 414 		object_class,
 415 		PROP_WEEK_START_DAY,
 416 		g_param_spec_int (
 417 			"week-start-day",
 418 			"Week Start Day",
 419 			NULL,
 420 			0,  /* Monday */
 421 			6,  /* Sunday */
 422 			0,
 423 			G_PARAM_READWRITE));
 424 
 425 	g_object_class_install_property (
 426 		object_class,
 427 		PROP_TWODIGIT_YEAR_CAN_FUTURE,
 428 		g_param_spec_boolean (
 429 			"twodigit-year-can-future",
 430 			"Two-digit year can be treated as future",
 431 			NULL,
 432 			TRUE,
 433 			G_PARAM_READWRITE));
 434 
 435 	g_object_class_install_property (
 436 		object_class,
 437 		PROP_SET_NONE,
 438 		g_param_spec_boolean (
 439 			"set-none",
 440 			"Sets None as selected date",
 441 			NULL,
 442 			FALSE,
 443 			G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
 444 
 445 	signals[CHANGED] = g_signal_new (
 446 		"changed",
 447 		G_OBJECT_CLASS_TYPE (object_class),
 448 		G_SIGNAL_RUN_FIRST,
 449 		G_STRUCT_OFFSET (EDateEditClass, changed),
 450 		NULL, NULL,
 451 		g_cclosure_marshal_VOID__VOID,
 452 		G_TYPE_NONE, 0);
 453 }
 454 
 455 static void
 456 e_date_edit_init (EDateEdit *dedit)
 457 {
 458 	dedit->priv = E_DATE_EDIT_GET_PRIVATE (dedit);
 459 
 460 	dedit->priv->show_date = TRUE;
 461 	dedit->priv->show_time = TRUE;
 462 	dedit->priv->use_24_hour_format = TRUE;
 463 
 464 	dedit->priv->make_time_insensitive = FALSE;
 465 
 466 	dedit->priv->lower_hour = 0;
 467 	dedit->priv->upper_hour = 24;
 468 
 469 	dedit->priv->date_is_valid = TRUE;
 470 	dedit->priv->date_set_to_none = TRUE;
 471 	dedit->priv->time_is_valid = TRUE;
 472 	dedit->priv->time_set_to_none = TRUE;
 473 	dedit->priv->time_callback = NULL;
 474 	dedit->priv->time_callback_data = NULL;
 475 	dedit->priv->time_callback_destroy = NULL;
 476 
 477 	dedit->priv->twodigit_year_can_future = TRUE;
 478 	dedit->priv->has_been_changed = FALSE;
 479 
 480 	create_children (dedit);
 481 
 482 	/* Set it to the current time. */
 483 	e_date_edit_set_time (dedit, 0);
 484 
 485 	e_extensible_load_extensions (E_EXTENSIBLE (dedit));
 486 }
 487 
 488 /**
 489  * e_date_edit_new:
 490  *
 491  * Description: Creates a new #EDateEdit widget which can be used
 492  * to provide an easy to use way for entering dates and times.
 493  *
 494  * Returns: a new #EDateEdit widget.
 495  */
 496 GtkWidget *
 497 e_date_edit_new (void)
 498 {
 499 	EDateEdit *dedit;
 500 	AtkObject *a11y;
 501 
 502 	dedit = g_object_new (E_TYPE_DATE_EDIT, NULL);
 503 	a11y = gtk_widget_get_accessible (GTK_WIDGET (dedit));
 504 	atk_object_set_name (a11y, _("Date and Time"));
 505 
 506 	return GTK_WIDGET (dedit);
 507 }
 508 
 509 static void
 510 create_children (EDateEdit *dedit)
 511 {
 512 	EDateEditPrivate *priv;
 513 	ECalendar *calendar;
 514 	GtkWidget *frame, *arrow;
 515 	GtkWidget *vbox, *bbox;
 516 	GtkWidget *child;
 517 	AtkObject *a11y;
 518 	GtkListStore *time_store;
 519 	GList *cells;
 520 
 521 	priv = dedit->priv;
 522 
 523 	priv->date_entry  = gtk_entry_new ();
 524 	a11y = gtk_widget_get_accessible (priv->date_entry);
 525 	atk_object_set_description (a11y, _("Text entry to input date"));
 526 	atk_object_set_name (a11y, _("Date"));
 527 	gtk_box_pack_start (GTK_BOX (dedit), priv->date_entry, FALSE, TRUE, 0);
 528 	gtk_widget_set_size_request (priv->date_entry, 100, -1);
 529 
 530 	g_signal_connect (
 531 		priv->date_entry, "key_press_event",
 532 		G_CALLBACK (on_date_entry_key_press), dedit);
 533 	g_signal_connect (
 534 		priv->date_entry, "key_release_event",
 535 		G_CALLBACK (on_date_entry_key_release), dedit);
 536 	g_signal_connect_after (
 537 		priv->date_entry, "focus_out_event",
 538 		G_CALLBACK (on_date_entry_focus_out), dedit);
 539 
 540 	priv->date_button = gtk_button_new ();
 541 	g_signal_connect (
 542 		priv->date_button, "clicked",
 543 		G_CALLBACK (on_date_button_clicked), dedit);
 544 	gtk_box_pack_start (
 545 		GTK_BOX (dedit), priv->date_button,
 546 		FALSE, FALSE, 0);
 547 	a11y = gtk_widget_get_accessible (priv->date_button);
 548 	atk_object_set_description (a11y, _("Click this button to show a calendar"));
 549 	atk_object_set_name (a11y, _("Date"));
 550 
 551 	arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
 552 	gtk_container_add (GTK_CONTAINER (priv->date_button), arrow);
 553 	gtk_widget_show (arrow);
 554 
 555 	if (priv->show_date) {
 556 		gtk_widget_show (priv->date_entry);
 557 		gtk_widget_show (priv->date_button);
 558 	}
 559 
 560 	/* This is just to create a space between the date & time parts. */
 561 	priv->space = gtk_drawing_area_new ();
 562 	gtk_box_pack_start (GTK_BOX (dedit), priv->space, FALSE, FALSE, 2);
 563 
 564 	gtk_rc_parse_string (
 565 		"style \"e-dateedit-timecombo-style\" {\n"
 566 		"  GtkComboBox::appears-as-list = 1\n"
 567 		"}\n"
 568 		"\n"
 569 		"widget \"*.e-dateedit-timecombo\" style \"e-dateedit-timecombo-style\"");
 570 
 571 	time_store = gtk_list_store_new (1, G_TYPE_STRING);
 572 	priv->time_combo = gtk_combo_box_new_with_model_and_entry (
 573 		GTK_TREE_MODEL (time_store));
 574 	gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (priv->time_combo), 0);
 575 	g_object_unref (time_store);
 576 
 577 	child = gtk_bin_get_child (GTK_BIN (priv->time_combo));
 578 
 579 	/* We need to make sure labels are right-aligned, since we want
 580 	 * digits to line up, and with a nonproportional font, the width
 581 	 * of a space != width of a digit.  Technically, only 12-hour
 582 	 * format needs this, but we do it always, for consistency. */
 583 	g_object_set (child, "xalign", 1.0, NULL);
 584 	cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->time_combo));
 585 	if (cells) {
 586 		g_object_set (GTK_CELL_RENDERER (cells->data), "xalign", 1.0, NULL);
 587 		g_list_free (cells);
 588 	}
 589 
 590 	gtk_box_pack_start (GTK_BOX (dedit), priv->time_combo, FALSE, TRUE, 0);
 591 	gtk_widget_set_size_request (priv->time_combo, 110, -1);
 592 	gtk_widget_set_name (priv->time_combo, "e-dateedit-timecombo");
 593 	rebuild_time_popup (dedit);
 594 	a11y = gtk_widget_get_accessible (priv->time_combo);
 595 	atk_object_set_description (a11y, _("Drop-down combination box to select time"));
 596 	atk_object_set_name (a11y, _("Time"));
 597 
 598 	g_signal_connect (
 599 		child, "key_press_event",
 600 		G_CALLBACK (on_time_entry_key_press), dedit);
 601 	g_signal_connect (
 602 		child, "key_release_event",
 603 		G_CALLBACK (on_time_entry_key_release), dedit);
 604 	g_signal_connect_after (
 605 		child, "focus_out_event",
 606 		G_CALLBACK (on_time_entry_focus_out), dedit);
 607 	g_signal_connect_after (
 608 		priv->time_combo, "changed",
 609 		G_CALLBACK (on_date_edit_time_selected), dedit);
 610 
 611 	if (priv->show_time || priv->make_time_insensitive)
 612 		gtk_widget_show (priv->time_combo);
 613 
 614 	if (!priv->show_time && priv->make_time_insensitive)
 615 		gtk_widget_set_sensitive (priv->time_combo, FALSE);
 616 
 617 	if (priv->show_date
 618 	    && (priv->show_time || priv->make_time_insensitive))
 619 		gtk_widget_show (priv->space);
 620 
 621 	priv->cal_popup = gtk_window_new (GTK_WINDOW_POPUP);
 622 	gtk_window_set_type_hint (
 623 		GTK_WINDOW (priv->cal_popup),
 624 		GDK_WINDOW_TYPE_HINT_COMBO);
 625 	gtk_widget_set_events (
 626 		priv->cal_popup,
 627 		gtk_widget_get_events (priv->cal_popup)
 628 		| GDK_KEY_PRESS_MASK);
 629 	g_signal_connect (
 630 		priv->cal_popup, "delete_event",
 631 		G_CALLBACK (on_date_popup_delete_event), dedit);
 632 	g_signal_connect (
 633 		priv->cal_popup, "key_press_event",
 634 		G_CALLBACK (on_date_popup_key_press), dedit);
 635 	g_signal_connect (
 636 		priv->cal_popup, "button_press_event",
 637 		G_CALLBACK (on_date_popup_button_press), dedit);
 638 	gtk_window_set_resizable (GTK_WINDOW (priv->cal_popup), TRUE);
 639 
 640 	frame = gtk_frame_new (NULL);
 641 	gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
 642 	gtk_container_add (GTK_CONTAINER (priv->cal_popup), frame);
 643 	gtk_widget_show (frame);
 644 
 645 	vbox = gtk_vbox_new (FALSE, 0);
 646 	gtk_container_add (GTK_CONTAINER (frame), vbox);
 647 	gtk_widget_show (vbox);
 648 
 649 	priv->calendar = e_calendar_new ();
 650 	calendar = E_CALENDAR (priv->calendar);
 651 	gnome_canvas_item_set (
 652 		GNOME_CANVAS_ITEM (calendar->calitem),
 653 		"maximum_days_selected", 1,
 654 		"move_selection_when_moving", FALSE,
 655 		NULL);
 656 
 657 	g_signal_connect (
 658 		calendar->calitem, "selection_changed",
 659 		G_CALLBACK (on_date_popup_date_selected), dedit);
 660 
 661 	gtk_box_pack_start (GTK_BOX (vbox), priv->calendar, FALSE, FALSE, 0);
 662 	gtk_widget_show (priv->calendar);
 663 
 664 	bbox = gtk_hbutton_box_new ();
 665 	gtk_container_set_border_width (GTK_CONTAINER (bbox), 4);
 666 	gtk_box_set_spacing (GTK_BOX (bbox), 2);
 667 	gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
 668 	gtk_widget_show (bbox);
 669 
 670 	priv->now_button = gtk_button_new_with_mnemonic (_("No_w"));
 671 	gtk_container_add (GTK_CONTAINER (bbox), priv->now_button);
 672 	gtk_widget_show (priv->now_button);
 673 	g_signal_connect (
 674 		priv->now_button, "clicked",
 675 		G_CALLBACK (on_date_popup_now_button_clicked), dedit);
 676 
 677 	priv->today_button = gtk_button_new_with_mnemonic (_("_Today"));
 678 	gtk_container_add (GTK_CONTAINER (bbox), priv->today_button);
 679 	gtk_widget_show (priv->today_button);
 680 	g_signal_connect (
 681 		priv->today_button, "clicked",
 682 		G_CALLBACK (on_date_popup_today_button_clicked), dedit);
 683 
 684 	/* Note that we don't show this here, since by default a 'None' date
 685 	 * is not permitted. */
 686 	priv->none_button = gtk_button_new_with_mnemonic (_("_None"));
 687 	gtk_container_add (GTK_CONTAINER (bbox), priv->none_button);
 688 	g_signal_connect (
 689 		priv->none_button, "clicked",
 690 		G_CALLBACK (on_date_popup_none_button_clicked), dedit);
 691 	g_object_bind_property (
 692 		dedit, "allow-no-date-set",
 693 		priv->none_button, "visible",
 694 		G_BINDING_SYNC_CREATE);
 695 }
 696 
 697 /* GtkWidget::mnemonic_activate() handler for the EDateEdit */
 698 static gboolean
 699 e_date_edit_mnemonic_activate (GtkWidget *widget,
 700                                gboolean group_cycling)
 701 {
 702 	e_date_edit_grab_focus (widget);
 703 	return TRUE;
 704 }
 705 
 706 /* Grab_focus handler for the EDateEdit. If the date field is being shown, we
 707  * grab the focus to that, otherwise we grab it to the time field. */
 708 static void
 709 e_date_edit_grab_focus (GtkWidget *widget)
 710 {
 711 	EDateEdit *dedit;
 712 	GtkWidget *child;
 713 
 714 	g_return_if_fail (E_IS_DATE_EDIT (widget));
 715 
 716 	dedit = E_DATE_EDIT (widget);
 717 	child = gtk_bin_get_child (GTK_BIN (dedit->priv->time_combo));
 718 
 719 	if (dedit->priv->show_date)
 720 		gtk_widget_grab_focus (dedit->priv->date_entry);
 721 	else
 722 		gtk_widget_grab_focus (child);
 723 }
 724 
 725 /**
 726  * e_date_edit_set_editable:
 727  * @dedit: an #EDateEdit widget.
 728  * @editable: whether or not the widget should accept edits.
 729  *
 730  * Allows the programmer to disallow editing (and the popping up of
 731  * the calendar widget), while still allowing the user to select the
 732  * date from the GtkEntry.
 733  */
 734 void
 735 e_date_edit_set_editable (EDateEdit *dedit,
 736                           gboolean editable)
 737 {
 738 	EDateEditPrivate *priv;
 739 
 740 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
 741 
 742 	priv = dedit->priv;
 743 
 744 	gtk_editable_set_editable (GTK_EDITABLE (priv->date_entry), editable);
 745 	gtk_widget_set_sensitive (priv->date_button, editable);
 746 }
 747 
 748 /**
 749  * e_date_edit_get_time:
 750  * @dedit: an #EDateEdit widget.
 751  * @the_time: returns the last valid time entered.
 752  * @Returns: the last valid time entered, or -1 if the time is not set.
 753  *
 754  * Returns the last valid time entered. If empty times are valid, by calling
 755  * e_date_edit_set_allow_no_date_set(), then it may return -1.
 756  *
 757  * Note that the last time entered may actually have been invalid. You can
 758  * check this with e_date_edit_time_is_valid().
 759  */
 760 time_t
 761 e_date_edit_get_time (EDateEdit *dedit)
 762 {
 763 	EDateEditPrivate *priv;
 764 	struct tm tmp_tm = { 0 };
 765 
 766 	g_return_val_if_fail (E_IS_DATE_EDIT (dedit), -1);
 767 
 768 	priv = dedit->priv;
 769 
 770 	/* Try to parse any new value now. */
 771 	e_date_edit_check_date_changed (dedit);
 772 	e_date_edit_check_time_changed (dedit);
 773 
 774 	if (priv->date_set_to_none)
 775 		return -1;
 776 
 777 	tmp_tm.tm_year = priv->year;
 778 	tmp_tm.tm_mon = priv->month;
 779 	tmp_tm.tm_mday = priv->day;
 780 
 781 	if (!priv->show_time || priv->time_set_to_none) {
 782 		tmp_tm.tm_hour = 0;
 783 		tmp_tm.tm_min = 0;
 784 	} else {
 785 		tmp_tm.tm_hour = priv->hour;
 786 		tmp_tm.tm_min = priv->minute;
 787 	}
 788 	tmp_tm.tm_sec = 0;
 789 	tmp_tm.tm_isdst = -1;
 790 
 791 	return mktime (&tmp_tm);
 792 }
 793 
 794 /**
 795  * e_date_edit_set_time:
 796  * @dedit: the EDateEdit widget
 797  * @the_time: The time and date that should be set on the widget
 798  *
 799  * Description:  Changes the displayed date and time in the EDateEdit
 800  * widget to be the one represented by @the_time.  If @the_time is 0
 801  * then current time is used. If it is -1, then the date is set to None.
 802  *
 803  * Note that the time is converted to local time using the Unix timezone,
 804  * so if you are using your own timezones then you should use
 805  * e_date_edit_set_date() and e_date_edit_set_time_of_day() instead.
 806  */
 807 void
 808 e_date_edit_set_time (EDateEdit *dedit,
 809                       time_t the_time)
 810 {
 811 	EDateEditPrivate *priv;
 812 	struct tm tmp_tm;
 813 	gboolean date_changed = FALSE, time_changed = FALSE;
 814 
 815 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
 816 
 817 	priv = dedit->priv;
 818 
 819 	if (the_time == -1) {
 820 		date_changed = e_date_edit_set_date_internal (
 821 			dedit, TRUE,
 822 			TRUE, 0, 0, 0);
 823 		time_changed = e_date_edit_set_time_internal (
 824 			dedit, TRUE,
 825 			TRUE, 0, 0);
 826 	} else {
 827 		if (the_time == 0) {
 828 			if (priv->time_callback) {
 829 				tmp_tm = (*priv->time_callback) (dedit, priv->time_callback_data);
 830 			} else {
 831 				the_time = time (NULL);
 832 				tmp_tm = *localtime (&the_time);
 833 			}
 834 		} else {
 835 			tmp_tm = *localtime (&the_time);
 836 		}
 837 
 838 		date_changed = e_date_edit_set_date_internal (
 839 			dedit, TRUE,
 840 			FALSE,
 841 			tmp_tm.tm_year,
 842 			tmp_tm.tm_mon,
 843 			tmp_tm.tm_mday);
 844 		time_changed = e_date_edit_set_time_internal (
 845 			dedit, TRUE,
 846 			FALSE,
 847 			tmp_tm.tm_hour,
 848 			tmp_tm.tm_min);
 849 	}
 850 
 851 	e_date_edit_update_date_entry (dedit);
 852 	e_date_edit_update_time_entry (dedit);
 853 	e_date_edit_update_time_combo_state (dedit);
 854 
 855 	/* Emit the signals if the date and/or time has actually changed. */
 856 	if (date_changed || time_changed)
 857 		g_signal_emit (dedit, signals[CHANGED], 0);
 858 }
 859 
 860 /**
 861  * e_date_edit_get_date:
 862  * @dedit: an #EDateEdit widget.
 863  * @year: returns the year set.
 864  * @month: returns the month set (1 - 12).
 865  * @day: returns the day set (1 - 31).
 866  * @Returns: TRUE if a time was set, or FALSE if the field is empty or 'None'.
 867  *
 868  * Returns the last valid date entered into the date field.
 869  */
 870 gboolean
 871 e_date_edit_get_date (EDateEdit *dedit,
 872                       gint *year,
 873                       gint *month,
 874                       gint *day)
 875 {
 876 	EDateEditPrivate *priv;
 877 
 878 	g_return_val_if_fail (E_IS_DATE_EDIT (dedit), FALSE);
 879 
 880 	priv = dedit->priv;
 881 
 882 	/* Try to parse any new value now. */
 883 	e_date_edit_check_date_changed (dedit);
 884 
 885 	*year = priv->year + 1900;
 886 	*month = priv->month + 1;
 887 	*day = priv->day;
 888 
 889 	if (priv->date_set_to_none
 890 	    && e_date_edit_get_allow_no_date_set (dedit))
 891 		return FALSE;
 892 
 893 	return TRUE;
 894 }
 895 
 896 /**
 897  * e_date_edit_set_date:
 898  * @dedit: an #EDateEdit widget.
 899  * @year: the year to set.
 900  * @month: the month to set (1 - 12).
 901  * @day: the day to set (1 - 31).
 902  *
 903  * Sets the date in the date field.
 904  */
 905 void
 906 e_date_edit_set_date (EDateEdit *dedit,
 907                       gint year,
 908                       gint month,
 909                       gint day)
 910 {
 911 	gboolean date_changed = FALSE;
 912 
 913 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
 914 
 915 	date_changed = e_date_edit_set_date_internal (
 916 		dedit, TRUE, FALSE,
 917 		year - 1900, month - 1,
 918 		day);
 919 
 920 	e_date_edit_update_date_entry (dedit);
 921 	e_date_edit_update_time_combo_state (dedit);
 922 
 923 	/* Emit the signals if the date has actually changed. */
 924 	if (date_changed)
 925 		g_signal_emit (dedit, signals[CHANGED], 0);
 926 }
 927 
 928 /**
 929  * e_date_edit_get_time_of_day:
 930  * @dedit: an #EDateEdit widget.
 931  * @hour: returns the hour set, or 0 if the time isn't set.
 932  * @minute: returns the minute set, or 0 if the time isn't set.
 933  * @Returns: TRUE if a time was set, or FALSE if the field is empty or 'None'.
 934  *
 935  * Returns the last valid time entered into the time field.
 936  */
 937 gboolean
 938 e_date_edit_get_time_of_day (EDateEdit *dedit,
 939                              gint *hour,
 940                              gint *minute)
 941 {
 942 	EDateEditPrivate *priv;
 943 
 944 	g_return_val_if_fail (E_IS_DATE_EDIT (dedit), FALSE);
 945 
 946 	priv = dedit->priv;
 947 
 948 	/* Try to parse any new value now. */
 949 	e_date_edit_check_time_changed (dedit);
 950 
 951 	if (priv->time_set_to_none) {
 952 		*hour = 0;
 953 		*minute = 0;
 954 		return FALSE;
 955 	} else {
 956 		*hour = priv->hour;
 957 		*minute = priv->minute;
 958 		return TRUE;
 959 	}
 960 }
 961 
 962 /**
 963  * e_date_edit_set_time_of_day:
 964  * @dedit: an #EDateEdit widget.
 965  * @hour: the hour to set, or -1 to set the time to None (i.e. empty).
 966  * @minute: the minute to set.
 967  *
 968  * Description: Sets the time in the time field.
 969  */
 970 void
 971 e_date_edit_set_time_of_day (EDateEdit *dedit,
 972                              gint hour,
 973                              gint minute)
 974 {
 975 	EDateEditPrivate *priv;
 976 	gboolean time_changed = FALSE;
 977 
 978 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
 979 
 980 	priv = dedit->priv;
 981 
 982 	if (hour == -1) {
 983 		gboolean allow_no_date_set = e_date_edit_get_allow_no_date_set (dedit);
 984 		g_return_if_fail (allow_no_date_set);
 985 		if (!priv->time_set_to_none) {
 986 			priv->time_set_to_none = TRUE;
 987 			time_changed = TRUE;
 988 		}
 989 	} else if (priv->time_set_to_none
 990 		   || priv->hour != hour
 991 		   || priv->minute != minute) {
 992 		priv->time_set_to_none = FALSE;
 993 		priv->hour = hour;
 994 		priv->minute = minute;
 995 		time_changed = TRUE;
 996 	}
 997 
 998 	e_date_edit_update_time_entry (dedit);
 999 
1000 	if (time_changed)
1001 		g_signal_emit (dedit, signals[CHANGED], 0);
1002 }
1003 
1004 void
1005 e_date_edit_set_date_and_time_of_day (EDateEdit *dedit,
1006                                       gint year,
1007                                       gint month,
1008                                       gint day,
1009                                       gint hour,
1010                                       gint minute)
1011 {
1012 	gboolean date_changed, time_changed;
1013 
1014 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
1015 
1016 	date_changed = e_date_edit_set_date_internal (
1017 		dedit, TRUE, FALSE,
1018 		year - 1900, month - 1, day);
1019 	time_changed = e_date_edit_set_time_internal (
1020 		dedit, TRUE, FALSE,
1021 		hour, minute);
1022 
1023 	e_date_edit_update_date_entry (dedit);
1024 	e_date_edit_update_time_entry (dedit);
1025 	e_date_edit_update_time_combo_state (dedit);
1026 
1027 	if (date_changed || time_changed)
1028 		g_signal_emit (dedit, signals[CHANGED], 0);
1029 }
1030 
1031 /**
1032  * e_date_edit_get_show_date:
1033  * @dedit: an #EDateEdit widget.
1034  * @Returns: Whether the date field is shown.
1035  *
1036  * Description: Returns TRUE if the date field is currently shown.
1037  */
1038 gboolean
1039 e_date_edit_get_show_date (EDateEdit *dedit)
1040 {
1041 	g_return_val_if_fail (E_IS_DATE_EDIT (dedit), TRUE);
1042 
1043 	return dedit->priv->show_date;
1044 }
1045 
1046 /**
1047  * e_date_edit_set_show_date:
1048  * @dedit: an #EDateEdit widget.
1049  * @show_date: TRUE if the date field should be shown.
1050  *
1051  * Description: Specifies whether the date field should be shown. The date
1052  * field would be hidden if only a time needed to be entered.
1053  */
1054 void
1055 e_date_edit_set_show_date (EDateEdit *dedit,
1056                            gboolean show_date)
1057 {
1058 	EDateEditPrivate *priv;
1059 
1060 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
1061 
1062 	priv = dedit->priv;
1063 
1064 	if (priv->show_date == show_date)
1065 		return;
1066 
1067 	priv->show_date = show_date;
1068 
1069 	if (show_date) {
1070 		gtk_widget_show (priv->date_entry);
1071 		gtk_widget_show (priv->date_button);
1072 	} else {
1073 		gtk_widget_hide (priv->date_entry);
1074 		gtk_widget_hide (priv->date_button);
1075 	}
1076 
1077 	e_date_edit_update_time_combo_state (dedit);
1078 
1079 	if (priv->show_date
1080 	    && (priv->show_time || priv->make_time_insensitive))
1081 		gtk_widget_show (priv->space);
1082 	else
1083 		gtk_widget_hide (priv->space);
1084 
1085 	g_object_notify (G_OBJECT (dedit), "show-date");
1086 }
1087 
1088 /**
1089  * e_date_edit_get_show_time:
1090  * @dedit: an #EDateEdit widget
1091  * @Returns: Whether the time field is shown.
1092  *
1093  * Description: Returns TRUE if the time field is currently shown.
1094  */
1095 gboolean
1096 e_date_edit_get_show_time (EDateEdit *dedit)
1097 {
1098 	g_return_val_if_fail (E_IS_DATE_EDIT (dedit), TRUE);
1099 
1100 	return dedit->priv->show_time;
1101 }
1102 
1103 /**
1104  * e_date_edit_set_show_time:
1105  * @dedit: an #EDateEdit widget
1106  * @show_time: TRUE if the time field should be shown.
1107  *
1108  * Description: Specifies whether the time field should be shown. The time
1109  * field would be hidden if only a date needed to be entered.
1110  */
1111 void
1112 e_date_edit_set_show_time (EDateEdit *dedit,
1113                            gboolean show_time)
1114 {
1115 	EDateEditPrivate *priv;
1116 
1117 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
1118 
1119 	priv = dedit->priv;
1120 
1121 	if (priv->show_time == show_time)
1122 		return;
1123 
1124 	priv->show_time = show_time;
1125 
1126 	e_date_edit_update_time_combo_state (dedit);
1127 
1128 	g_object_notify (G_OBJECT (dedit), "show-time");
1129 }
1130 
1131 /**
1132  * e_date_edit_get_make_time_insensitive:
1133  * @dedit: an #EDateEdit widget
1134  * @Returns: Whether the time field is be made insensitive instead of hiding
1135  * it.
1136  *
1137  * Description: Returns TRUE if the time field is made insensitive instead of
1138  * hiding it.
1139  */
1140 gboolean
1141 e_date_edit_get_make_time_insensitive (EDateEdit *dedit)
1142 {
1143 	g_return_val_if_fail (E_IS_DATE_EDIT (dedit), TRUE);
1144 
1145 	return dedit->priv->make_time_insensitive;
1146 }
1147 
1148 /**
1149  * e_date_edit_set_make_time_insensitive:
1150  * @dedit: an #EDateEdit widget
1151  * @make_insensitive: TRUE if the time field should be made insensitive instead
1152  * of hiding it.
1153  *
1154  * Description: Specifies whether the time field should be made insensitive
1155  * rather than hiding it. Note that this doesn't make it insensitive - you
1156  * need to call e_date_edit_set_show_time() with FALSE as show_time to do that.
1157  *
1158  * This is useful if you want to disable the time field, but don't want it to
1159  * disappear as that may affect the layout of the widgets.
1160  */
1161 void
1162 e_date_edit_set_make_time_insensitive (EDateEdit *dedit,
1163                                        gboolean make_insensitive)
1164 {
1165 	EDateEditPrivate *priv;
1166 
1167 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
1168 
1169 	priv = dedit->priv;
1170 
1171 	if (priv->make_time_insensitive == make_insensitive)
1172 		return;
1173 
1174 	priv->make_time_insensitive = make_insensitive;
1175 
1176 	e_date_edit_update_time_combo_state (dedit);
1177 }
1178 
1179 /**
1180  * e_date_edit_get_week_start_day:
1181  * @dedit: an #EDateEdit widget
1182  * @Returns: the week start day, from 0 (Monday) to 6 (Sunday).
1183  *
1184  * Description: Returns the week start day currently used in the calendar
1185  * popup.
1186  */
1187 gint
1188 e_date_edit_get_week_start_day (EDateEdit *dedit)
1189 {
1190 	gint week_start_day;
1191 
1192 	g_return_val_if_fail (E_IS_DATE_EDIT (dedit), 1);
1193 
1194 	g_object_get (
1195 		E_CALENDAR (dedit->priv->calendar)->calitem,
1196 		"week_start_day", &week_start_day, NULL);
1197 
1198 	return week_start_day;
1199 }
1200 
1201 /**
1202  * e_date_edit_set_week_start_day:
1203  * @dedit: an #EDateEdit widget
1204  * @week_start_day: the week start day, from 0 (Monday) to 6 (Sunday).
1205  *
1206  * Description: Sets the week start day to use in the calendar popup.
1207  */
1208 void
1209 e_date_edit_set_week_start_day (EDateEdit *dedit,
1210                                 gint week_start_day)
1211 {
1212 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
1213 
1214 	gnome_canvas_item_set (
1215 		GNOME_CANVAS_ITEM (E_CALENDAR (dedit->priv->calendar)->calitem),
1216 		"week_start_day", week_start_day, NULL);
1217 
1218 	g_object_notify (G_OBJECT (dedit), "week-start-day");
1219 }
1220 
1221 /* Whether we show week numbers in the date popup. */
1222 gboolean
1223 e_date_edit_get_show_week_numbers (EDateEdit *dedit)
1224 {
1225 	gboolean show_week_numbers;
1226 
1227 	g_return_val_if_fail (E_IS_DATE_EDIT (dedit), FALSE);
1228 
1229 	g_object_get (
1230 		E_CALENDAR (dedit->priv->calendar)->calitem,
1231 		"show_week_numbers", &show_week_numbers, NULL);
1232 
1233 	return show_week_numbers;
1234 }
1235 
1236 void
1237 e_date_edit_set_show_week_numbers (EDateEdit *dedit,
1238                                    gboolean show_week_numbers)
1239 {
1240 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
1241 
1242 	gnome_canvas_item_set (
1243 		GNOME_CANVAS_ITEM (E_CALENDAR (dedit->priv->calendar)->calitem),
1244 		"show_week_numbers", show_week_numbers, NULL);
1245 
1246 	g_object_notify (G_OBJECT (dedit), "show-week-numbers");
1247 }
1248 
1249 /* Whether we use 24 hour format in the time field & popup. */
1250 gboolean
1251 e_date_edit_get_use_24_hour_format (EDateEdit *dedit)
1252 {
1253 	g_return_val_if_fail (E_IS_DATE_EDIT (dedit), TRUE);
1254 
1255 	return dedit->priv->use_24_hour_format;
1256 }
1257 
1258 void
1259 e_date_edit_set_use_24_hour_format (EDateEdit *dedit,
1260                                     gboolean use_24_hour_format)
1261 {
1262 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
1263 
1264 	if (dedit->priv->use_24_hour_format == use_24_hour_format)
1265 		return;
1266 
1267 	dedit->priv->use_24_hour_format = use_24_hour_format;
1268 
1269 	rebuild_time_popup (dedit);
1270 
1271 	e_date_edit_update_time_entry (dedit);
1272 
1273 	g_object_notify (G_OBJECT (dedit), "use-24-hour-format");
1274 }
1275 
1276 /* Whether we allow the date to be set to 'None'. e_date_edit_get_time() will
1277  * return (time_t) -1 in this case. */
1278 gboolean
1279 e_date_edit_get_allow_no_date_set (EDateEdit *dedit)
1280 {
1281 	g_return_val_if_fail (E_IS_DATE_EDIT (dedit), FALSE);
1282 
1283 	return dedit->priv->allow_no_date_set;
1284 }
1285 
1286 void
1287 e_date_edit_set_allow_no_date_set (EDateEdit *dedit,
1288                                    gboolean allow_no_date_set)
1289 {
1290 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
1291 
1292 	if (dedit->priv->allow_no_date_set == allow_no_date_set)
1293 		return;
1294 
1295 	dedit->priv->allow_no_date_set = allow_no_date_set;
1296 
1297 	if (!allow_no_date_set) {
1298 		/* If the date is showing, we make sure it isn't 'None' (we
1299 		 * don't really mind if the time is empty), else if just the
1300 		 * time is showing we make sure it isn't 'None'. */
1301 		if (dedit->priv->show_date) {
1302 			if (dedit->priv->date_set_to_none)
1303 				e_date_edit_set_time (dedit, 0);
1304 		} else {
1305 			if (dedit->priv->time_set_to_none)
1306 				e_date_edit_set_time (dedit, 0);
1307 		}
1308 	}
1309 
1310 	g_object_notify (G_OBJECT (dedit), "allow-no-date-set");
1311 }
1312 
1313 /* The range of time to show in the time combo popup. */
1314 void
1315 e_date_edit_get_time_popup_range (EDateEdit *dedit,
1316                                   gint *lower_hour,
1317                                   gint *upper_hour)
1318 {
1319 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
1320 
1321 	*lower_hour = dedit->priv->lower_hour;
1322 	*upper_hour = dedit->priv->upper_hour;
1323 }
1324 
1325 void
1326 e_date_edit_set_time_popup_range (EDateEdit *dedit,
1327                                   gint lower_hour,
1328                                   gint upper_hour)
1329 {
1330 	EDateEditPrivate *priv;
1331 
1332 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
1333 
1334 	priv = dedit->priv;
1335 
1336 	if (priv->lower_hour == lower_hour
1337 	    && priv->upper_hour == upper_hour)
1338 		return;
1339 
1340 	priv->lower_hour = lower_hour;
1341 	priv->upper_hour = upper_hour;
1342 
1343 	rebuild_time_popup (dedit);
1344 
1345 	/* Setting the combo list items seems to mess up the time entry, so
1346 	 * we set it again. We have to reset it to its last valid time. */
1347 	priv->time_is_valid = TRUE;
1348 	e_date_edit_update_time_entry (dedit);
1349 }
1350 
1351 /* The arrow button beside the date field has been clicked, so we show the
1352  * popup with the ECalendar in. */
1353 static void
1354 on_date_button_clicked (GtkWidget *widget,
1355                         EDateEdit *dedit)
1356 {
1357 	e_date_edit_show_date_popup (dedit);
1358 }
1359 
1360 static void
1361 e_date_edit_show_date_popup (EDateEdit *dedit)
1362 {
1363 	EDateEditPrivate *priv;
1364 	ECalendar *calendar;
1365 	GdkWindow *window;
1366 	struct tm mtm;
1367 	const gchar *date_text;
1368 	GDate selected_day;
1369 	gboolean clear_selection = FALSE;
1370 
1371 	priv = dedit->priv;
1372 	calendar = E_CALENDAR (priv->calendar);
1373 
1374 	date_text = gtk_entry_get_text (GTK_ENTRY (priv->date_entry));
1375 	if (field_set_to_none (date_text)
1376 	    || !e_date_edit_parse_date (dedit, date_text, &mtm))
1377 		clear_selection = TRUE;
1378 
1379 	if (clear_selection) {
1380 		e_calendar_item_set_selection (calendar->calitem, NULL, NULL);
1381 	} else {
1382 		g_date_clear (&selected_day, 1);
1383 		g_date_set_dmy (
1384 			&selected_day, mtm.tm_mday, mtm.tm_mon + 1,
1385 			mtm.tm_year + 1900);
1386 		e_calendar_item_set_selection (
1387 			calendar->calitem,
1388 			&selected_day, NULL);
1389 	}
1390 
1391 	/* FIXME: Hack. Change ECalendarItem so it doesn't queue signal
1392 	 * emissions. */
1393 	calendar->calitem->selection_changed = FALSE;
1394 
1395 	position_date_popup (dedit);
1396 	gtk_widget_show (priv->cal_popup);
1397 	gtk_widget_grab_focus (priv->cal_popup);
1398 	gtk_grab_add (priv->cal_popup);
1399 
1400 	window = gtk_widget_get_window (priv->cal_popup);
1401 	gdk_pointer_grab (
1402 		window, TRUE,
1403 		GDK_BUTTON_PRESS_MASK |
1404 		GDK_BUTTON_RELEASE_MASK |
1405 		GDK_POINTER_MOTION_MASK,
1406 		NULL, NULL, GDK_CURRENT_TIME);
1407 	gdk_keyboard_grab (window, TRUE, GDK_CURRENT_TIME);
1408 	gdk_window_focus (window, GDK_CURRENT_TIME);
1409 }
1410 
1411 /* This positions the date popup below and to the left of the arrow button,
1412  * just before it is shown. */
1413 static void
1414 position_date_popup (EDateEdit *dedit)
1415 {
1416 	gint x, y;
1417 	gint win_x, win_y;
1418 	gint bwidth, bheight;
1419 	GtkWidget *toplevel;
1420 	GdkWindow *window;
1421 	GtkRequisition cal_req, button_req;
1422 	gint screen_width, screen_height;
1423 
1424 	gtk_widget_get_preferred_size (dedit->priv->cal_popup, &cal_req, NULL);
1425 
1426 	gtk_widget_get_preferred_size (dedit->priv->date_button, &button_req, NULL);
1427 	bwidth = button_req.width;
1428 	gtk_widget_get_preferred_size (
1429 		gtk_widget_get_parent (dedit->priv->date_button), &button_req, NULL);
1430 	bheight = button_req.height;
1431 
1432 	gtk_widget_translate_coordinates (
1433 		dedit->priv->date_button,
1434 		gtk_widget_get_toplevel (dedit->priv->date_button),
1435 		bwidth - cal_req.width, bheight, &x, &y);
1436 
1437 	toplevel = gtk_widget_get_toplevel (dedit->priv->date_button);
1438 	window = gtk_widget_get_window (toplevel);
1439 	gdk_window_get_origin (window, &win_x, &win_y);
1440 
1441 	x += win_x;
1442 	y += win_y;
1443 
1444 	screen_width = gdk_screen_width ();
1445 	screen_height = gdk_screen_height ();
1446 
1447 	x = CLAMP (x, 0, MAX (0, screen_width - cal_req.width));
1448 	y = CLAMP (y, 0, MAX (0, screen_height - cal_req.height));
1449 
1450 	gtk_window_move (GTK_WINDOW (dedit->priv->cal_popup), x, y);
1451 }
1452 
1453 /* A date has been selected in the date popup, so we set the date field
1454  * and hide the popup. */
1455 static void
1456 on_date_popup_date_selected (ECalendarItem *calitem,
1457                              EDateEdit *dedit)
1458 {
1459 	GDate start_date, end_date;
1460 
1461 	hide_date_popup (dedit);
1462 
1463 	if (!e_calendar_item_get_selection (calitem, &start_date, &end_date))
1464 		return;
1465 
1466 	e_date_edit_set_date (
1467 		dedit, g_date_get_year (&start_date),
1468 		g_date_get_month (&start_date),
1469 		g_date_get_day (&start_date));
1470 }
1471 
1472 static void
1473 on_date_popup_now_button_clicked (GtkWidget *button,
1474                                   EDateEdit *dedit)
1475 {
1476 	hide_date_popup (dedit);
1477 	e_date_edit_set_time (dedit, 0);
1478 }
1479 
1480 static void
1481 on_date_popup_today_button_clicked (GtkWidget *button,
1482                                     EDateEdit *dedit)
1483 {
1484 	EDateEditPrivate *priv;
1485 	struct tm tmp_tm;
1486 	time_t t;
1487 
1488 	priv = dedit->priv;
1489 
1490 	hide_date_popup (dedit);
1491 
1492 	if (priv->time_callback) {
1493 		tmp_tm = (*priv->time_callback) (dedit, priv->time_callback_data);
1494 	} else {
1495 		t = time (NULL);
1496 		tmp_tm = *localtime (&t);
1497 	}
1498 
1499 	e_date_edit_set_date (
1500 		dedit, tmp_tm.tm_year + 1900,
1501 		tmp_tm.tm_mon + 1, tmp_tm.tm_mday);
1502 }
1503 
1504 static void
1505 on_date_popup_none_button_clicked (GtkWidget *button,
1506                                    EDateEdit *dedit)
1507 {
1508 	hide_date_popup (dedit);
1509 	e_date_edit_set_time (dedit, -1);
1510 }
1511 
1512 /* A key has been pressed while the date popup is showing. If it is the Escape
1513  * key we hide the popup. */
1514 static gint
1515 on_date_popup_key_press (GtkWidget *widget,
1516                          GdkEventKey *event,
1517                          EDateEdit *dedit)
1518 {
1519 	GdkWindow *window;
1520 
1521 	window = gtk_widget_get_window (dedit->priv->cal_popup);
1522 
1523 	if (event->keyval != GDK_KEY_Escape) {
1524 		gdk_keyboard_grab (window, TRUE, GDK_CURRENT_TIME);
1525 		return FALSE;
1526 	}
1527 
1528 	g_signal_stop_emission_by_name (widget, "key_press_event");
1529 	hide_date_popup (dedit);
1530 
1531 	return TRUE;
1532 }
1533 
1534 /* A mouse button has been pressed while the date popup is showing.
1535  * Any button press events used to select days etc. in the popup will have
1536  * have been handled elsewhere, so here we just hide the popup.
1537  * (This function is yanked from gtkcombo.c) */
1538 static gint
1539 on_date_popup_button_press (GtkWidget *widget,
1540                             GdkEventButton *event,
1541                             gpointer data)
1542 {
1543 	EDateEdit *dedit;
1544 	GtkWidget *child;
1545 
1546 	dedit = data;
1547 
1548 	child = gtk_get_event_widget ((GdkEvent *) event);
1549 
1550 	/* We don't ask for button press events on the grab widget, so
1551 	 *  if an event is reported directly to the grab widget, it must
1552 	 *  be on a window outside the application (and thus we remove
1553 	 *  the popup window). Otherwise, we check if the widget is a child
1554 	 *  of the grab widget, and only remove the popup window if it
1555 	 *  is not.
1556 	 */
1557 	if (child != widget) {
1558 		while (child) {
1559 			if (child == widget)
1560 				return FALSE;
1561 			child = gtk_widget_get_parent (child);
1562 		}
1563 	}
1564 
1565 	hide_date_popup (dedit);
1566 
1567 	return TRUE;
1568 }
1569 
1570 /* A delete event has been received for the date popup, so we hide it and
1571  * return TRUE so it doesn't get destroyed. */
1572 static gint
1573 on_date_popup_delete_event (GtkWidget *widget,
1574                             EDateEdit *dedit)
1575 {
1576 	hide_date_popup (dedit);
1577 	return TRUE;
1578 }
1579 
1580 /* Hides the date popup, removing any grabs. */
1581 static void
1582 hide_date_popup (EDateEdit *dedit)
1583 {
1584 	gtk_widget_hide (dedit->priv->cal_popup);
1585 	gtk_grab_remove (dedit->priv->cal_popup);
1586 	gdk_pointer_ungrab (GDK_CURRENT_TIME);
1587 	gdk_keyboard_ungrab (GDK_CURRENT_TIME);
1588 }
1589 
1590 /* Clears the time popup and rebuilds it using the lower_hour, upper_hour
1591  * and use_24_hour_format settings. */
1592 static void
1593 rebuild_time_popup (EDateEdit *dedit)
1594 {
1595 	EDateEditPrivate *priv;
1596 	GtkTreeModel *model;
1597 	GtkListStore *list_store;
1598 	GtkTreeIter iter;
1599 	gchar buffer[40];
1600 	struct tm tmp_tm;
1601 	gint hour, min;
1602 
1603 	priv = dedit->priv;
1604 
1605 	model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->time_combo));
1606 	list_store = GTK_LIST_STORE (model);
1607 	gtk_list_store_clear (list_store);
1608 
1609 	/* Fill the struct tm with some sane values. */
1610 	tmp_tm.tm_year = 2000;
1611 	tmp_tm.tm_mon = 0;
1612 	tmp_tm.tm_mday = 1;
1613 	tmp_tm.tm_sec  = 0;
1614 	tmp_tm.tm_isdst = 0;
1615 
1616 	for (hour = priv->lower_hour; hour <= priv->upper_hour; hour++) {
1617 
1618 		/* We don't want to display midnight at the end,
1619 		 * since that is really in the next day. */
1620 		if (hour == 24)
1621 			break;
1622 
1623 		/* We want to finish on upper_hour, with min == 0. */
1624 		for (min = 0;
1625 		     min == 0 || (min < 60 && hour != priv->upper_hour);
1626 		     min += 30) {
1627 			tmp_tm.tm_hour = hour;
1628 			tmp_tm.tm_min  = min;
1629 
1630 			if (priv->use_24_hour_format)
1631 				/* This is a strftime() format.
1632 				 * %H = hour (0-23), %M = minute. */
1633 				e_time_format_time (
1634 					&tmp_tm, 1, 0,
1635 					buffer, sizeof (buffer));
1636 			else
1637 				/* This is a strftime() format.
1638 				 * %I = hour (1-12), %M = minute,
1639 				 * %p = am/pm string. */
1640 				e_time_format_time (
1641 					&tmp_tm, 0, 0,
1642 					buffer, sizeof (buffer));
1643 
1644 			/* For 12-hour am/pm format, we want space padding,
1645 			 * not zero padding. This can be done with strftime's
1646 			 * %l, but it's a potentially unportable extension. */
1647 			if (!priv->use_24_hour_format && buffer[0] == '0')
1648 				buffer[0] = ' ';
1649 
1650 			gtk_list_store_append (list_store, &iter);
1651 			gtk_list_store_set (list_store, &iter, 0, buffer, -1);
1652 		}
1653 	}
1654 }
1655 
1656 static gboolean
1657 e_date_edit_parse_date (EDateEdit *dedit,
1658                         const gchar *date_text,
1659                         struct tm *date_tm)
1660 {
1661 	gboolean twodigit_year = FALSE;
1662 
1663 	if (e_time_parse_date_ex (date_text, date_tm, &twodigit_year) != E_TIME_PARSE_OK)
1664 		return FALSE;
1665 
1666 	if (twodigit_year && !dedit->priv->twodigit_year_can_future) {
1667 		time_t t = time (NULL);
1668 		struct tm *today_tm = localtime (&t);
1669 
1670 		/* It was only 2 digit year in dedit and it was interpreted as
1671 		 * in the future, but we don't want it as this, so decrease by
1672 		 * 100 years to last century. */
1673 		if (date_tm->tm_year > today_tm->tm_year)
1674 			date_tm->tm_year -= 100;
1675 	}
1676 
1677 	return TRUE;
1678 }
1679 
1680 static gboolean
1681 e_date_edit_parse_time (EDateEdit *dedit,
1682                         const gchar *time_text,
1683                         struct tm *time_tm)
1684 {
1685 	if (field_set_to_none (time_text)) {
1686 		time_tm->tm_hour = 0;
1687 		time_tm->tm_min = 0;
1688 		return TRUE;
1689 	}
1690 
1691 	if (e_time_parse_time (time_text, time_tm) != E_TIME_PARSE_OK)
1692 		return FALSE;
1693 
1694 	return TRUE;
1695 }
1696 
1697 /* Returns TRUE if the string is empty or is "None" in the current locale.
1698  * It ignores whitespace. */
1699 static gboolean
1700 field_set_to_none (const gchar *text)
1701 {
1702 	const gchar *pos;
1703 	const gchar *none_string;
1704 	gint n;
1705 
1706 	pos = text;
1707 	while (n = (gint)((guchar) * pos), isspace (n))
1708 		pos++;
1709 
1710 	/* Translators: "None" for date field of a date edit, shown when
1711 	 * there is no date set. */
1712 	none_string = C_("date", "None");
1713 
1714 	if (*pos == '\0' || !strncmp (pos, none_string, strlen (none_string)))
1715 		return TRUE;
1716 	return FALSE;
1717 }
1718 
1719 static void
1720 on_date_edit_time_selected (GtkComboBox *combo,
1721                             EDateEdit *dedit)
1722 {
1723 	GtkWidget *child;
1724 
1725 	child = gtk_bin_get_child (GTK_BIN (combo));
1726 
1727 	/* We only want to emit signals when an item is selected explicitly,
1728 	 * not when it is selected by the silly combo update thing. */
1729 	if (gtk_combo_box_get_active (combo) == -1)
1730 		return;
1731 
1732 	if (!gtk_widget_get_mapped (child))
1733 		return;
1734 
1735 	e_date_edit_check_time_changed (dedit);
1736 }
1737 
1738 static gint
1739 on_date_entry_key_press (GtkWidget *widget,
1740                          GdkEventKey *event,
1741                          EDateEdit *dedit)
1742 {
1743 	if (event->state & GDK_MOD1_MASK
1744 	    && (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_Down
1745 		|| event->keyval == GDK_KEY_Return)) {
1746 		g_signal_stop_emission_by_name (
1747 			widget, "key_press_event");
1748 		e_date_edit_show_date_popup (dedit);
1749 		return TRUE;
1750 	}
1751 
1752 	/* If the user hits the return key emit a "date_changed" signal if
1753 	 * needed. But let the signal carry on. */
1754 	if (event->keyval == GDK_KEY_Return) {
1755 		e_date_edit_check_date_changed (dedit);
1756 		return FALSE;
1757 	}
1758 
1759 	return FALSE;
1760 }
1761 
1762 static gint
1763 on_time_entry_key_press (GtkWidget *widget,
1764                          GdkEventKey *event,
1765                          EDateEdit *dedit)
1766 {
1767 	GtkWidget *child;
1768 
1769 	child = gtk_bin_get_child (GTK_BIN (dedit->priv->time_combo));
1770 
1771 	/* I'd like to use Alt+Up/Down for popping up the list, like Win32,
1772 	 * but the combo steals any Up/Down keys, so we use Alt + Return. */
1773 #if 0
1774 	if (event->state & GDK_MOD1_MASK
1775 	    && (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_Down)) {
1776 #else
1777 	if (event->state & GDK_MOD1_MASK && event->keyval == GDK_KEY_Return) {
1778 #endif
1779 		g_signal_stop_emission_by_name (widget, "key_press_event");
1780 		g_signal_emit_by_name (child, "activate", 0);
1781 		return TRUE;
1782 	}
1783 
1784 	/* Stop the return key from emitting the activate signal, and check
1785 	 * if we need to emit a "time_changed" signal. */
1786 	if (event->keyval == GDK_KEY_Return) {
1787 		g_signal_stop_emission_by_name (
1788 			widget,
1789 						"key_press_event");
1790 		e_date_edit_check_time_changed (dedit);
1791 		return TRUE;
1792 	}
1793 
1794 	return FALSE;
1795 }
1796 
1797 static gint
1798 on_date_entry_key_release (GtkWidget *widget,
1799                            GdkEventKey *event,
1800                            EDateEdit *dedit)
1801 {
1802 	e_date_edit_check_date_changed (dedit);
1803 	return TRUE;
1804 }
1805 
1806 static gint
1807 on_time_entry_key_release (GtkWidget *widget,
1808                            GdkEventKey *event,
1809                            EDateEdit *dedit)
1810 {
1811 	if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_Down) {
1812 		g_signal_stop_emission_by_name (
1813 			widget,
1814 						"key_release_event");
1815 		e_date_edit_check_time_changed (dedit);
1816 		return TRUE;
1817 	}
1818 
1819 	return FALSE;
1820 }
1821 
1822 static gint
1823 on_date_entry_focus_out (GtkEntry *entry,
1824                          GdkEventFocus *event,
1825                          EDateEdit *dedit)
1826 {
1827 	struct tm tmp_tm;
1828 	GtkWidget *msg_dialog;
1829 
1830 	tmp_tm.tm_year = 0;
1831 	tmp_tm.tm_mon = 0;
1832 	tmp_tm.tm_mday = 0;
1833 
1834 	e_date_edit_check_date_changed (dedit);
1835 
1836 	if (!e_date_edit_date_is_valid (dedit)) {
1837 		msg_dialog = gtk_message_dialog_new (
1838 			NULL,
1839 			GTK_DIALOG_MODAL,
1840 			GTK_MESSAGE_WARNING,
1841 			GTK_BUTTONS_OK,
1842 			"%s", _("Invalid Date Value"));
1843 		gtk_dialog_run (GTK_DIALOG (msg_dialog));
1844 		gtk_widget_destroy (msg_dialog);
1845 		e_date_edit_get_date (
1846 			dedit, &tmp_tm.tm_year,
1847 			&tmp_tm.tm_mon, &tmp_tm.tm_mday);
1848 		e_date_edit_set_date (
1849 			dedit, tmp_tm.tm_year,
1850 			tmp_tm.tm_mon, tmp_tm.tm_mday);
1851 		gtk_widget_grab_focus (GTK_WIDGET (entry));
1852 		return FALSE;
1853 	} else if (e_date_edit_get_date (
1854 		dedit, &tmp_tm.tm_year, &tmp_tm.tm_mon, &tmp_tm.tm_mday)) {
1855 
1856 		e_date_edit_set_date (
1857 			dedit,tmp_tm.tm_year,tmp_tm.tm_mon,tmp_tm.tm_mday);
1858 
1859 		if (dedit->priv->has_been_changed) {
1860 			/* The previous one didn't emit changed signal,
1861 			 * but we want it even here, thus doing itself. */
1862 			g_signal_emit (dedit, signals[CHANGED], 0);
1863 			dedit->priv->has_been_changed = FALSE;
1864 		}
1865 	} else {
1866 		dedit->priv->date_set_to_none = TRUE;
1867 		e_date_edit_update_date_entry (dedit);
1868 	}
1869 	return FALSE;
1870 }
1871 
1872 static gint
1873 on_time_entry_focus_out (GtkEntry *entry,
1874                          GdkEventFocus *event,
1875                          EDateEdit *dedit)
1876 {
1877 	GtkWidget *msg_dialog;
1878 
1879 	e_date_edit_check_time_changed (dedit);
1880 
1881 	if (!e_date_edit_time_is_valid (dedit)) {
1882 		msg_dialog = gtk_message_dialog_new (
1883 			NULL,
1884 			GTK_DIALOG_MODAL,
1885 			GTK_MESSAGE_WARNING,
1886 			GTK_BUTTONS_OK,
1887 			"%s", _("Invalid Time Value"));
1888 		gtk_dialog_run (GTK_DIALOG (msg_dialog));
1889 		gtk_widget_destroy (msg_dialog);
1890 		e_date_edit_set_time (dedit,e_date_edit_get_time (dedit));
1891 		gtk_widget_grab_focus (GTK_WIDGET (entry));
1892 		return FALSE;
1893 	}
1894 	return FALSE;
1895 }
1896 
1897 static void
1898 add_relation (EDateEdit *dedit,
1899               GtkWidget *widget)
1900 {
1901 	AtkObject *a11yEdit, *a11yWidget;
1902 	AtkRelationSet *set;
1903 	AtkRelation *relation;
1904 	GPtrArray *target;
1905 	gpointer target_object;
1906 
1907 	/* add a labelled_by relation for widget for accessibility */
1908 
1909 	a11yEdit = gtk_widget_get_accessible (GTK_WIDGET (dedit));
1910 	a11yWidget = gtk_widget_get_accessible (widget);
1911 
1912 	set = atk_object_ref_relation_set (a11yWidget);
1913 	if (set != NULL) {
1914 		relation = atk_relation_set_get_relation_by_type (
1915 			set, ATK_RELATION_LABELLED_BY);
1916 		/* check whether has a labelled_by relation already */
1917 		if (relation != NULL)
1918 			return;
1919 	}
1920 
1921 	set = atk_object_ref_relation_set (a11yEdit);
1922 	if (!set)
1923 		return;
1924 
1925 	relation = atk_relation_set_get_relation_by_type (
1926 		set, ATK_RELATION_LABELLED_BY);
1927 	if (relation != NULL) {
1928 		target = atk_relation_get_target (relation);
1929 		target_object = g_ptr_array_index (target, 0);
1930 		if (ATK_IS_OBJECT (target_object)) {
1931 			atk_object_add_relationship (
1932 				a11yWidget,
1933 					ATK_RELATION_LABELLED_BY,
1934 					ATK_OBJECT (target_object));
1935 		}
1936 	}
1937 }
1938 
1939 /* This sets the text in the date entry according to the current settings. */
1940 static void
1941 e_date_edit_update_date_entry (EDateEdit *dedit)
1942 {
1943 	EDateEditPrivate *priv;
1944 	gchar buffer[100];
1945 	struct tm tmp_tm = { 0 };
1946 
1947 	priv = dedit->priv;
1948 
1949 	if (priv->date_set_to_none || !priv->date_is_valid) {
1950 		gtk_entry_set_text (GTK_ENTRY (priv->date_entry), C_("date", "None"));
1951 	} else {
1952 		/* This is a strftime() format for a short date.
1953 		 * %x the preferred date representation for the current locale
1954 		 * without the time, but is forced to use 4 digit year. */
1955 		gchar *format = e_time_get_d_fmt_with_4digit_year ();
1956 		time_t tt;
1957 
1958 		tmp_tm.tm_year = priv->year;
1959 		tmp_tm.tm_mon = priv->month;
1960 		tmp_tm.tm_mday = priv->day;
1961 		tmp_tm.tm_isdst = -1;
1962 
1963 		/* initialize all 'struct tm' members properly */
1964 		tt = mktime (&tmp_tm);
1965 		if (tt && localtime (&tt))
1966 			tmp_tm = *localtime (&tt);
1967 
1968 		e_utf8_strftime (buffer, sizeof (buffer), format, &tmp_tm);
1969 		g_free (format);
1970 		gtk_entry_set_text (GTK_ENTRY (priv->date_entry), buffer);
1971 	}
1972 
1973 	add_relation (dedit, priv->date_entry);
1974 	add_relation (dedit, priv->date_button);
1975 }
1976 
1977 /* This sets the text in the time entry according to the current settings. */
1978 static void
1979 e_date_edit_update_time_entry (EDateEdit *dedit)
1980 {
1981 	EDateEditPrivate *priv;
1982 	GtkComboBox *combo_box;
1983 	GtkWidget *child;
1984 	gchar buffer[40];
1985 	struct tm tmp_tm = { 0 };
1986 
1987 	priv = dedit->priv;
1988 
1989 	combo_box = GTK_COMBO_BOX (priv->time_combo);
1990 	child = gtk_bin_get_child (GTK_BIN (priv->time_combo));
1991 
1992 	if (priv->time_set_to_none || !priv->time_is_valid) {
1993 		gtk_combo_box_set_active (combo_box, -1);
1994 		gtk_entry_set_text (GTK_ENTRY (child), "");
1995 	} else {
1996 		GtkTreeModel *model;
1997 		GtkTreeIter iter;
1998 		gboolean valid;
1999 		gchar *b;
2000 
2001 		/* Set these to reasonable values just in case. */
2002 		tmp_tm.tm_year = 2000;
2003 		tmp_tm.tm_mon = 0;
2004 		tmp_tm.tm_mday = 1;
2005 
2006 		tmp_tm.tm_hour = priv->hour;
2007 		tmp_tm.tm_min = priv->minute;
2008 
2009 		tmp_tm.tm_sec = 0;
2010 		tmp_tm.tm_isdst = -1;
2011 
2012 		if (priv->use_24_hour_format)
2013 			/* This is a strftime() format.
2014 			 * %H = hour (0-23), %M = minute. */
2015 			e_time_format_time (
2016 				&tmp_tm, 1, 0, buffer, sizeof (buffer));
2017 		else
2018 			/* This is a strftime() format.
2019 			 * %I = hour (1-12), %M = minute, %p = am/pm. */
2020 			e_time_format_time (
2021 				&tmp_tm, 0, 0, buffer, sizeof (buffer));
2022 
2023 		/* For 12-hour am/pm format, we want space padding, not
2024 		 * zero padding.  This can be done with strftime's %l,
2025 		 * but it's a potentially unportable extension. */
2026 		if (!priv->use_24_hour_format && buffer[0] == '0')
2027 			buffer[0] = ' ';
2028 
2029 		gtk_entry_set_text (GTK_ENTRY (child), buffer);
2030 
2031 		/* truncate left spaces */
2032 		b = buffer;
2033 		while (*b == ' ')
2034 			b++;
2035 
2036 		model = gtk_combo_box_get_model (combo_box);
2037 		valid = gtk_tree_model_get_iter_first (model, &iter);
2038 
2039 		while (valid) {
2040 			gchar *text = NULL;
2041 
2042 			gtk_tree_model_get (model, &iter, 0, &text, -1);
2043 			if (text) {
2044 				gchar *t = text;
2045 
2046 				/* truncate left spaces */
2047 				while (*t == ' ')
2048 					t++;
2049 
2050 				if (strcmp (b, t) == 0) {
2051 					gtk_combo_box_set_active_iter (
2052 						combo_box, &iter);
2053 					g_free (text);
2054 					break;
2055 				}
2056 			}
2057 
2058 			g_free (text);
2059 
2060 			valid = gtk_tree_model_iter_next (model, &iter);
2061 		}
2062 	}
2063 
2064 	add_relation (dedit, priv->time_combo);
2065 }
2066 
2067 static void
2068 e_date_edit_update_time_combo_state (EDateEdit *dedit)
2069 {
2070 	EDateEditPrivate *priv;
2071 	gboolean show = TRUE, show_now_button = TRUE;
2072 	gboolean clear_entry = FALSE, sensitive = TRUE;
2073 	const gchar *text;
2074 
2075 	priv = dedit->priv;
2076 
2077 	/* If the date entry is currently shown, and it is set to None,
2078 	 * clear the time entry and disable the time combo. */
2079 	if (priv->show_date && priv->date_set_to_none) {
2080 		clear_entry = TRUE;
2081 		sensitive = FALSE;
2082 	}
2083 
2084 	if (!priv->show_time) {
2085 		if (priv->make_time_insensitive) {
2086 			clear_entry = TRUE;
2087 			sensitive = FALSE;
2088 		} else {
2089 			show = FALSE;
2090 		}
2091 
2092 		show_now_button = FALSE;
2093 	}
2094 
2095 	if (clear_entry) {
2096 		GtkWidget *child;
2097 
2098 		/* Only clear it if it isn't empty already. */
2099 		child = gtk_bin_get_child (GTK_BIN (priv->time_combo));
2100 		text = gtk_entry_get_text (GTK_ENTRY (child));
2101 		if (text[0])
2102 			gtk_entry_set_text (GTK_ENTRY (child), "");
2103 	}
2104 
2105 	gtk_widget_set_sensitive (priv->time_combo, sensitive);
2106 
2107 	if (show)
2108 		gtk_widget_show (priv->time_combo);
2109 	else
2110 		gtk_widget_hide (priv->time_combo);
2111 
2112 	if (show_now_button)
2113 		gtk_widget_show (priv->now_button);
2114 	else
2115 		gtk_widget_hide (priv->now_button);
2116 
2117 	if (priv->show_date
2118 	    && (priv->show_time || priv->make_time_insensitive))
2119 		gtk_widget_show (priv->space);
2120 	else
2121 		gtk_widget_hide (priv->space);
2122 }
2123 
2124 /* Parses the date, and if it is different from the current settings it
2125  * updates the settings and emits a "date_changed" signal. */
2126 static void
2127 e_date_edit_check_date_changed (EDateEdit *dedit)
2128 {
2129 	EDateEditPrivate *priv;
2130 	const gchar *date_text;
2131 	struct tm tmp_tm;
2132 	gboolean none = FALSE, valid = TRUE, date_changed = FALSE;
2133 
2134 	priv = dedit->priv;
2135 
2136 	tmp_tm.tm_year = 0;
2137 	tmp_tm.tm_mon = 0;
2138 	tmp_tm.tm_mday = 0;
2139 
2140 	date_text = gtk_entry_get_text (GTK_ENTRY (priv->date_entry));
2141 	if (field_set_to_none (date_text)) {
2142 		none = TRUE;
2143 	} else if (!e_date_edit_parse_date (dedit, date_text, &tmp_tm)) {
2144 		valid = FALSE;
2145 		tmp_tm.tm_year = 0;
2146 		tmp_tm.tm_mon = 0;
2147 		tmp_tm.tm_mday = 0;
2148 	}
2149 
2150 	date_changed = e_date_edit_set_date_internal (
2151 		dedit, valid, none,
2152 		tmp_tm.tm_year,
2153 		tmp_tm.tm_mon,
2154 		tmp_tm.tm_mday);
2155 
2156 	if (date_changed) {
2157 		priv->has_been_changed = TRUE;
2158 		g_signal_emit (dedit, signals[CHANGED], 0);
2159 	}
2160 }
2161 
2162 /* Parses the time, and if it is different from the current settings it
2163  * updates the settings and emits a "time_changed" signal. */
2164 static void
2165 e_date_edit_check_time_changed (EDateEdit *dedit)
2166 {
2167 	EDateEditPrivate *priv;
2168 	GtkWidget *child;
2169 	const gchar *time_text;
2170 	struct tm tmp_tm;
2171 	gboolean none = FALSE, valid = TRUE, time_changed;
2172 
2173 	priv = dedit->priv;
2174 
2175 	tmp_tm.tm_hour = 0;
2176 	tmp_tm.tm_min = 0;
2177 
2178 	child = gtk_bin_get_child (GTK_BIN (priv->time_combo));
2179 	time_text = gtk_entry_get_text (GTK_ENTRY (child));
2180 	if (field_set_to_none (time_text))
2181 		none = TRUE;
2182 	else if (!e_date_edit_parse_time (dedit, time_text, &tmp_tm))
2183 		valid = FALSE;
2184 
2185 	time_changed = e_date_edit_set_time_internal (
2186 		dedit, valid, none,
2187 		tmp_tm.tm_hour,
2188 		tmp_tm.tm_min);
2189 
2190 	if (time_changed) {
2191 		e_date_edit_update_time_entry (dedit);
2192 		g_signal_emit (dedit, signals[CHANGED], 0);
2193 	}
2194 }
2195 
2196 /**
2197  * e_date_edit_date_is_valid:
2198  * @dedit: an #EDateEdit widget.
2199  * @Returns: TRUE if the last date entered was valid.
2200  *
2201  * Returns TRUE if the last date entered was valid.
2202  *
2203  * Note that if this returns FALSE, you can still use e_date_edit_get_time()
2204  * or e_date_edit_get_date() to get the last time or date entered which was
2205  * valid.
2206  */
2207 gboolean
2208 e_date_edit_date_is_valid (EDateEdit *dedit)
2209 {
2210 	g_return_val_if_fail (E_IS_DATE_EDIT (dedit), FALSE);
2211 
2212 	if (!dedit->priv->date_is_valid)
2213 		return FALSE;
2214 
2215 	/* If the date is empty/None and that isn't permitted, return FALSE. */
2216 	if (dedit->priv->date_set_to_none
2217 	    && !e_date_edit_get_allow_no_date_set (dedit))
2218 		return FALSE;
2219 
2220 	return TRUE;
2221 }
2222 
2223 /**
2224  * e_date_edit_time_is_valid:
2225  * @dedit: an #EDateEdit widget.
2226  * @Returns: TRUE if the last time entered was valid.
2227  *
2228  * Returns TRUE if the last time entered was valid.
2229  *
2230  * Note that if this returns FALSE, you can still use e_date_edit_get_time()
2231  * or e_date_edit_get_time_of_day() to get the last time or time of the day
2232  * entered which was valid.
2233  */
2234 gboolean
2235 e_date_edit_time_is_valid (EDateEdit *dedit)
2236 {
2237 	g_return_val_if_fail (E_IS_DATE_EDIT (dedit), FALSE);
2238 
2239 	if (!dedit->priv->time_is_valid)
2240 		return FALSE;
2241 
2242 	/* If the time is empty and that isn't permitted, return FALSE.
2243 	 * Note that we don't mind an empty time if the date field is shown
2244 	 * - in that case we just assume 0:00. */
2245 	if (dedit->priv->time_set_to_none && !dedit->priv->show_date
2246 	    && !e_date_edit_get_allow_no_date_set (dedit))
2247 		return FALSE;
2248 
2249 	return TRUE;
2250 }
2251 
2252 static gboolean
2253 e_date_edit_set_date_internal (EDateEdit *dedit,
2254                                gboolean valid,
2255                                gboolean none,
2256                                gint year,
2257                                gint month,
2258                                gint day)
2259 {
2260 	EDateEditPrivate *priv;
2261 	gboolean date_changed = FALSE;
2262 
2263 	priv = dedit->priv;
2264 
2265 	if (!valid) {
2266 		/* Date is invalid. */
2267 		if (priv->date_is_valid) {
2268 			priv->date_is_valid = FALSE;
2269 			date_changed = TRUE;
2270 		}
2271 	} else if (none) {
2272 		/* Date has been set to 'None'. */
2273 		if (!priv->date_is_valid
2274 		    || !priv->date_set_to_none) {
2275 			priv->date_is_valid = TRUE;
2276 			priv->date_set_to_none = TRUE;
2277 			date_changed = TRUE;
2278 		}
2279 	} else {
2280 		/* Date has been set to a specific date. */
2281 		if (!priv->date_is_valid
2282 		    || priv->date_set_to_none
2283 		    || priv->year != year
2284 		    || priv->month != month
2285 		    || priv->day != day) {
2286 			priv->date_is_valid = TRUE;
2287 			priv->date_set_to_none = FALSE;
2288 			priv->year = year;
2289 			priv->month = month;
2290 			priv->day = day;
2291 			date_changed = TRUE;
2292 		}
2293 	}
2294 
2295 	return date_changed;
2296 }
2297 
2298 static gboolean
2299 e_date_edit_set_time_internal (EDateEdit *dedit,
2300                                gboolean valid,
2301                                gboolean none,
2302                                gint hour,
2303                                gint minute)
2304 {
2305 	EDateEditPrivate *priv;
2306 	gboolean time_changed = FALSE;
2307 
2308 	priv = dedit->priv;
2309 
2310 	if (!valid) {
2311 		/* Time is invalid. */
2312 		if (priv->time_is_valid) {
2313 			priv->time_is_valid = FALSE;
2314 			time_changed = TRUE;
2315 		}
2316 	} else if (none) {
2317 		/* Time has been set to empty/'None'. */
2318 		if (!priv->time_is_valid
2319 		    || !priv->time_set_to_none) {
2320 			priv->time_is_valid = TRUE;
2321 			priv->time_set_to_none = TRUE;
2322 			time_changed = TRUE;
2323 		}
2324 	} else {
2325 		/* Time has been set to a specific time. */
2326 		if (!priv->time_is_valid
2327 		    || priv->time_set_to_none
2328 		    || priv->hour != hour
2329 		    || priv->minute != minute) {
2330 			priv->time_is_valid = TRUE;
2331 			priv->time_set_to_none = FALSE;
2332 			priv->hour = hour;
2333 			priv->minute = minute;
2334 			time_changed = TRUE;
2335 		}
2336 	}
2337 
2338 	return time_changed;
2339 }
2340 
2341 gboolean
2342 e_date_edit_get_twodigit_year_can_future (EDateEdit *dedit)
2343 {
2344 	g_return_val_if_fail (E_IS_DATE_EDIT (dedit), FALSE);
2345 
2346 	return dedit->priv->twodigit_year_can_future;
2347 }
2348 
2349 void
2350 e_date_edit_set_twodigit_year_can_future (EDateEdit *dedit,
2351                                           gboolean value)
2352 {
2353 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
2354 
2355 	dedit->priv->twodigit_year_can_future = value;
2356 }
2357 
2358 /* Sets a callback to use to get the current time. This is useful if the
2359  * application needs to use its own timezone data rather than rely on the
2360  * Unix timezone. */
2361 void
2362 e_date_edit_set_get_time_callback (EDateEdit *dedit,
2363                                    EDateEditGetTimeCallback cb,
2364                                    gpointer data,
2365                                    GDestroyNotify destroy)
2366 {
2367 	EDateEditPrivate *priv;
2368 
2369 	g_return_if_fail (E_IS_DATE_EDIT (dedit));
2370 
2371 	priv = dedit->priv;
2372 
2373 	if (priv->time_callback_data && priv->time_callback_destroy)
2374 		(*priv->time_callback_destroy) (priv->time_callback_data);
2375 
2376 	priv->time_callback = cb;
2377 	priv->time_callback_data = data;
2378 	priv->time_callback_destroy = destroy;
2379 
2380 }
2381 
2382 GtkWidget *
2383 e_date_edit_get_entry (EDateEdit *dedit)
2384 {
2385 	g_return_val_if_fail (E_IS_DATE_EDIT (dedit), NULL);
2386 
2387 	return GTK_WIDGET (dedit->priv->date_entry);
2388 }