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 }