1 /*
2  * gnc-date-edit.c -- Date editor widget
3  *
4  * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
5  * All rights reserved.
6  *
7  * Gnucash is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public License
9  * as published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * Gnucash is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, contact:
19  *
20  * Free Software Foundation           Voice:  +1-617-542-5942
21  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
22  * Boston, MA  02110-1301,  USA       gnu@gnu.org
23  *
24  */
25 /*
26   @NOTATION@
27  */
28 
29 /*
30  * Date editor widget
31  *
32  * Authors: Miguel de Icaza
33  *          Dave Peticolas <dave@krondo.com>
34  */
35 
36 #include <config.h>
37 
38 #include <gtk/gtk.h>
39 #include <glib/gi18n.h>
40 #include <gdk/gdkkeysyms.h>
41 #include <string.h>
42 #include <stdlib.h> /* atoi */
43 #include <ctype.h> /* isdigit */
44 #include <stdio.h>
45 
46 #include "gnc-date.h"
47 #include "gnc-engine.h"
48 #include "dialog-utils.h"
49 #include "gnc-date-edit.h"
50 
51 enum
52 {
53     DATE_CHANGED,
54     TIME_CHANGED,
55     LAST_SIGNAL
56 };
57 
58 enum
59 {
60     PROP_0,
61     PROP_TIME,
62 };
63 
64 static QofLogModule log_module = GNC_MOD_GUI;
65 static guint date_edit_signals [LAST_SIGNAL] = { 0 };
66 
67 
68 static void gnc_date_edit_init         (GNCDateEdit      *gde);
69 static void gnc_date_edit_class_init   (GNCDateEditClass *klass);
70 static void gnc_date_edit_dispose      (GObject          *object);
71 static void gnc_date_edit_finalize     (GObject          *object);
72 static void gnc_date_edit_forall       (GtkContainer       *container,
73                                         gboolean	    include_internals,
74                                         GtkCallback	    callback,
75                                         gpointer	    callbabck_data);
76 static struct tm gnc_date_edit_get_date_internal (GNCDateEdit *gde);
77 static int date_accel_key_press(GtkWidget *widget,
78                                 GdkEventKey *event,
79                                 gpointer data);
80 
81 
82 static GtkBoxClass *parent_class;
83 
84 /**
85  * gnc_date_edit_get_type:
86  *
87  * Returns the GType for the GNCDateEdit widget
88  */
89 GType
gnc_date_edit_get_type(void)90 gnc_date_edit_get_type (void)
91 {
92     static GType date_edit_type = 0;
93 
94     if (date_edit_type == 0)
95     {
96         static const GTypeInfo date_edit_info =
97         {
98             sizeof (GNCDateEditClass),
99             NULL,
100             NULL,
101             (GClassInitFunc) gnc_date_edit_class_init,
102             NULL,
103             NULL,
104             sizeof (GNCDateEdit),
105             0, /* n_preallocs */
106             (GInstanceInitFunc) gnc_date_edit_init,
107             NULL,
108         };
109 
110         date_edit_type = g_type_register_static (GTK_TYPE_BOX,
111                          "GNCDateEdit",
112                          &date_edit_info, 0);
113     }
114 
115     return date_edit_type;
116 }
117 
118 
119 static char *
gnc_strtok_r(char * s,const char * delim,char ** save_ptr)120 gnc_strtok_r (char *s, const char *delim, char **save_ptr)
121 {
122     char *token;
123 
124     if (s == NULL)
125         s = *save_ptr;
126 
127     /* Scan leading delimiters.  */
128     s += strspn (s, delim);
129     if (!s || *s == '\0')
130         return NULL;
131 
132     /* Find the end of the token.  */
133     token = s;
134     s = strpbrk (token, delim);
135     if (s == NULL)
136         /* This token finishes the string.  */
137         *save_ptr = strchr (token, '\0');
138     else
139     {
140         /* Terminate the token and make *SAVE_PTR point past it.  */
141         *s = '\0';
142         *save_ptr = s + 1;
143     }
144     return token;
145 }
146 
147 static void
gnc_date_edit_popdown(GNCDateEdit * gde)148 gnc_date_edit_popdown(GNCDateEdit *gde)
149 {
150     GdkSeat *seat;
151     GdkDevice *pointer;
152     GdkWindow *window;
153 
154     g_return_if_fail (GNC_IS_DATE_EDIT (gde));
155 
156     ENTER("gde %p", gde);
157 
158     window = gtk_widget_get_window (GTK_WIDGET(gde));
159 
160     seat = gdk_display_get_default_seat (gdk_window_get_display (window));
161     pointer = gdk_seat_get_pointer (seat);
162 
163     gtk_grab_remove (gde->cal_popup);
164     gtk_widget_hide (gde->cal_popup);
165 
166     if (pointer)
167         gdk_seat_ungrab (seat);
168 
169     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gde->date_button),
170                                   FALSE);
171 
172     LEAVE(" ");
173 }
174 
175 static void
day_selected(GtkCalendar * calendar,GNCDateEdit * gde)176 day_selected (GtkCalendar *calendar, GNCDateEdit *gde)
177 {
178     time64 t;
179     guint year, month, day;
180     gde->in_selected_handler = TRUE;
181     gtk_calendar_get_date (calendar, &year, &month, &day);
182     /* GtkCalendar returns a 0-based month */
183     t = gnc_dmy2time64 (day, month + 1, year);
184     gnc_date_edit_set_time (gde, t);
185     gde->in_selected_handler = FALSE;
186 }
187 
188 static void
day_selected_double_click(GtkCalendar * calendar,GNCDateEdit * gde)189 day_selected_double_click (GtkCalendar *calendar, GNCDateEdit *gde)
190 {
191     gnc_date_edit_popdown (gde);
192 }
193 
194 static gint
delete_popup(GtkWidget * widget,gpointer data)195 delete_popup (GtkWidget *widget, gpointer data)
196 {
197     GNCDateEdit *gde;
198 
199     gde = data;
200     gnc_date_edit_popdown (gde);
201 
202     return TRUE;
203 }
204 
205 static gint
key_press_popup(GtkWidget * widget,GdkEventKey * event,gpointer data)206 key_press_popup (GtkWidget *widget, GdkEventKey *event, gpointer data)
207 {
208     GNCDateEdit *gde = data;
209 
210     if (event->keyval != GDK_KEY_Return &&
211             event->keyval != GDK_KEY_KP_Enter &&
212             event->keyval != GDK_KEY_Escape)
213         return date_accel_key_press(gde->date_entry, event, data);
214 
215     gde = data;
216     g_signal_stop_emission_by_name (G_OBJECT (widget), "key-press-event");
217     gnc_date_edit_popdown (gde);
218 
219     return TRUE;
220 }
221 
222 static void
position_popup(GNCDateEdit * gde)223 position_popup (GNCDateEdit *gde)
224 {
225     gint x, y;
226     gint bwidth, bheight;
227     GtkRequisition req;
228     GtkAllocation alloc;
229 
230     gtk_widget_get_preferred_size (gde->cal_popup, &req, NULL);
231 
232     gdk_window_get_origin (gtk_widget_get_window (gde->date_button), &x, &y);
233 
234     gtk_widget_get_allocation (gde->date_button, &alloc);
235     x += alloc.x;
236     y += alloc.y;
237     bwidth = alloc.width;
238     bheight = alloc.height;
239 
240     x += bwidth - req.width;
241     y += bheight;
242 
243     if (x < 0)
244         x = 0;
245 
246     if (y < 0)
247         y = 0;
248 
249     gtk_window_move (GTK_WINDOW (gde->cal_popup), x, y);
250 }
251 
252 /* Pulled from gtkcombobox.c */
253 static gboolean
popup_grab_on_window(GdkWindow * window,GdkDevice * keyboard,GdkDevice * pointer,guint32 activate_time)254 popup_grab_on_window (GdkWindow *window,
255                       GdkDevice *keyboard,
256                       GdkDevice *pointer,
257                       guint32    activate_time)
258 {
259     GdkDisplay *display = gdk_window_get_display (window);
260     GdkSeat *seat = gdk_display_get_default_seat (display);
261     GdkEvent *event = gtk_get_current_event ();
262 
263     if (keyboard && gdk_seat_grab (seat, window, GDK_SEAT_CAPABILITY_KEYBOARD, TRUE, NULL,
264                                    event, NULL, NULL) != GDK_GRAB_SUCCESS)
265         return FALSE;
266 
267     if (pointer && gdk_seat_grab (seat, window, GDK_SEAT_CAPABILITY_POINTER, TRUE, NULL,
268                                   event, NULL, NULL) != GDK_GRAB_SUCCESS)
269     {
270         if (keyboard)
271             gdk_seat_ungrab (seat);
272 
273         return FALSE;
274     }
275     return TRUE;
276 }
277 
278 
279 static void
gnc_date_edit_popup(GNCDateEdit * gde)280 gnc_date_edit_popup (GNCDateEdit *gde)
281 {
282     GtkWidget *toplevel;
283     struct tm mtm;
284     gboolean date_was_valid;
285     GdkDevice *device, *keyboard, *pointer;
286 
287     g_return_if_fail (GNC_IS_DATE_EDIT (gde));
288 
289     ENTER("gde %p", gde);
290 
291     device = gtk_get_current_event_device ();
292 
293     /* This code is pretty much just copied from gtk_date_edit_get_date */
294     date_was_valid = qof_scan_date (gtk_entry_get_text (GTK_ENTRY (gde->date_entry)),
295                                     &mtm.tm_mday, &mtm.tm_mon, &mtm.tm_year);
296     if (!date_was_valid)
297     {
298         /* No valid date. Hacky workaround: Instead of crashing we randomly choose today's date. */
299         gnc_tm_get_today_start(&mtm);
300     }
301 
302     mtm.tm_mon--;
303 
304     /* Hope the user does not actually mean years early in the A.D. days...
305      * This date widget will obviously not work for a history program :-)
306      */
307     if (mtm.tm_year >= 1900)
308         mtm.tm_year -= 1900;
309 
310     gnc_tm_set_day_start(&mtm);
311 
312     /* Set the calendar.  */
313     gtk_calendar_select_day (GTK_CALENDAR (gde->calendar), 1);
314     gtk_calendar_select_month (GTK_CALENDAR (gde->calendar), mtm.tm_mon,
315                                1900 + mtm.tm_year);
316     gtk_calendar_select_day (GTK_CALENDAR (gde->calendar), mtm.tm_mday);
317 
318     /* Make sure we'll get notified of clicks outside the popup
319      * window so we can properly pop down if that happens. */
320     toplevel = gtk_widget_get_toplevel (GTK_WIDGET (gde));
321     if (GTK_IS_WINDOW (toplevel))
322     {
323         gtk_window_group_add_window (
324             gtk_window_get_group (GTK_WINDOW (toplevel)),
325             GTK_WINDOW (gde->cal_popup));
326         gtk_window_set_transient_for (GTK_WINDOW (gde->cal_popup),
327                                       GTK_WINDOW (toplevel));
328     }
329 
330     position_popup (gde);
331 
332     gtk_widget_show (gde->cal_popup);
333 
334     gtk_widget_grab_focus (gde->cal_popup);
335     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gde->date_button),
336                                   TRUE);
337 
338     if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
339     {
340         keyboard = device;
341         pointer = gdk_device_get_associated_device (device);
342     }
343     else
344     {
345         pointer = device;
346         keyboard = gdk_device_get_associated_device (device);
347     }
348 
349     if (!gtk_widget_has_focus (gde->calendar))
350         gtk_widget_grab_focus (gde->calendar);
351 
352     if (!popup_grab_on_window (gtk_widget_get_window ((GTK_WIDGET(gde->cal_popup))),
353                                keyboard, pointer, GDK_CURRENT_TIME))
354     {
355         gtk_widget_hide (gde->cal_popup);
356         LEAVE("Failed to grab window");
357         return;
358     }
359 
360     gtk_grab_add (gde->cal_popup);
361 
362     LEAVE(" ");
363 }
364 
365 /* This function is a customized gtk_combo_box_list_button_pressed(). */
366 static gboolean
gnc_date_edit_button_pressed(GtkWidget * widget,GdkEventButton * event,gpointer data)367 gnc_date_edit_button_pressed (GtkWidget      *widget,
368                               GdkEventButton *event,
369                               gpointer        data)
370 {
371     GNCDateEdit *gde     = GNC_DATE_EDIT(data);
372     GtkWidget   *ewidget = gtk_get_event_widget ((GdkEvent *)event);
373 
374     ENTER("widget=%p, ewidget=%p, event=%p, gde=%p", widget, ewidget, event, gde);
375 
376     /* While popped up, ignore presses outside the popup window. */
377     if (ewidget == gde->cal_popup)
378     {
379         LEAVE("Press on calendar. Ignoring.");
380         return TRUE;
381     }
382 
383     /* If the press isn't to make the popup appear, just propagate it. */
384     if (ewidget != gde->date_button ||
385             gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gde->date_button)))
386     {
387         LEAVE("Press, not on popup button, or while popup is raised.");
388         return FALSE;
389     }
390 
391     if (!gtk_widget_has_focus (gde->date_button))
392         gtk_widget_grab_focus (gde->date_button);
393 
394     gde->popup_in_progress = TRUE;
395 
396     gnc_date_edit_popup (gde);
397 
398     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gde->date_button), TRUE);
399 
400     LEAVE("Popup in progress.");
401     return TRUE;
402 }
403 
404 static gboolean
gnc_date_edit_button_released(GtkWidget * widget,GdkEventButton * event,gpointer data)405 gnc_date_edit_button_released (GtkWidget      *widget,
406                                GdkEventButton *event,
407                                gpointer        data)
408 {
409     GNCDateEdit *gde     = GNC_DATE_EDIT(data);
410     GtkWidget   *ewidget = gtk_get_event_widget ((GdkEvent *)event);
411     gboolean popup_in_progress = FALSE;
412 
413     ENTER("widget=%p, ewidget=%p, event=%p, gde=%p", widget, ewidget, event, gde);
414 
415     if (gde->popup_in_progress)
416     {
417         popup_in_progress = TRUE;
418         gde->popup_in_progress = FALSE;
419     }
420 
421     /* Propagate releases on the calendar. */
422     if (ewidget == gde->calendar)
423     {
424         LEAVE("Button release on calendar.");
425         return FALSE;
426     }
427 
428     if (ewidget == gde->date_button)
429     {
430         /* Pop down if we're up and it isn't due to the preceding press. */
431         if (!popup_in_progress &&
432                 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gde->date_button)))
433         {
434             gnc_date_edit_popdown (gde);
435             LEAVE("Release on button, not in progress. Popped down.");
436             return TRUE;
437         }
438 
439         LEAVE("Button release on button. Allowing.");
440         return FALSE;
441     }
442 
443     /* Pop down on a release anywhere else. */
444     gnc_date_edit_popdown (gde);
445     LEAVE("Release not on button or calendar. Popping down.");
446     return TRUE;
447 }
448 
449 static void
gnc_date_edit_button_toggled(GtkWidget * widget,GNCDateEdit * gde)450 gnc_date_edit_button_toggled (GtkWidget *widget, GNCDateEdit *gde)
451 {
452     ENTER("widget %p, gde %p", widget, gde);
453 
454     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
455     {
456         if (!gde->popup_in_progress)
457             gnc_date_edit_popup (gde);
458     }
459     else
460         gnc_date_edit_popdown (gde);
461 
462     LEAVE(" ");
463 }
464 
465 static void
set_time(GtkWidget * widget,GNCDateEdit * gde)466 set_time (GtkWidget *widget, GNCDateEdit *gde)
467 {
468     gchar *text;
469     GtkTreeModel *model;
470     GtkTreeIter iter;
471 
472     model = gtk_combo_box_get_model(GTK_COMBO_BOX(gde->time_combo));
473     gtk_combo_box_get_active_iter (GTK_COMBO_BOX(gde->time_combo), &iter);
474     gtk_tree_model_get( model, &iter, 0, &text, -1 );
475 
476     gtk_entry_set_text (GTK_ENTRY (gde->time_entry), text);
477     if(text)
478         g_free(text);
479     g_signal_emit (G_OBJECT (gde), date_edit_signals [TIME_CHANGED], 0);
480 }
481 
482 static void
fill_time_combo(GtkWidget * widget,GNCDateEdit * gde)483 fill_time_combo (GtkWidget *widget, GNCDateEdit *gde)
484 {
485     GtkTreeModel *model;
486     GtkTreeIter  hour_iter, min_iter;
487     struct tm *tm_returned;
488     struct tm mtm;
489     time64 current_time;
490     int i, j;
491 
492     if (gde->lower_hour > gde->upper_hour)
493         return;
494 
495     model = gtk_combo_box_get_model (GTK_COMBO_BOX(gde->time_combo));
496 
497     gnc_time (&current_time);
498     tm_returned = gnc_localtime_r (&current_time, &mtm);
499     g_return_if_fail(tm_returned != NULL);
500 
501     for (i = gde->lower_hour; i <= gde->upper_hour; i++)
502     {
503         char buffer [40];
504         mtm.tm_hour = i;
505         mtm.tm_min  = 0;
506 
507         if (gde->flags & GNC_DATE_EDIT_24_HR)
508             qof_strftime (buffer, sizeof (buffer), "%H:00", &mtm);
509         else
510             qof_strftime (buffer, sizeof (buffer), "%I:00 %p ", &mtm);
511 
512         gtk_tree_store_append (GTK_TREE_STORE(model), &hour_iter, NULL);
513         gtk_tree_store_set (GTK_TREE_STORE(model), &hour_iter, 0, buffer, -1);
514 
515         for (j = 0; j < 60; j += 15)
516         {
517             mtm.tm_min = j;
518 
519             if (gde->flags & GNC_DATE_EDIT_24_HR)
520                 qof_strftime (buffer, sizeof (buffer), "%H:%M", &mtm);
521             else
522                 qof_strftime (buffer, sizeof (buffer), "%I:%M %p", &mtm);
523 
524             gtk_tree_store_append(GTK_TREE_STORE(model), &min_iter, &hour_iter );
525             gtk_tree_store_set (GTK_TREE_STORE(model), &min_iter, 0, buffer, -1);
526         }
527     }
528 }
529 
530 static void
gnc_date_edit_set_time_internal(GNCDateEdit * gde,time64 the_time)531 gnc_date_edit_set_time_internal (GNCDateEdit *gde, time64 the_time)
532 {
533     char buffer [MAX_DATE_LENGTH + 1];
534     struct tm *mytm = gnc_localtime (&the_time);
535 
536     g_return_if_fail(mytm != NULL);
537 
538     /* Update the date text. */
539     qof_print_date_dmy_buff(buffer, MAX_DATE_LENGTH,
540                             mytm->tm_mday,
541                             mytm->tm_mon + 1,
542                             1900 + mytm->tm_year);
543     gtk_entry_set_text(GTK_ENTRY(gde->date_entry), buffer);
544 
545     /* Update the calendar. */
546     if (!gde->in_selected_handler)
547     {
548 	gtk_calendar_select_day(GTK_CALENDAR (gde->calendar), 1);
549 	gtk_calendar_select_month(GTK_CALENDAR (gde->calendar),
550 				  mytm->tm_mon, 1900 + mytm->tm_year);
551 	gtk_calendar_select_day(GTK_CALENDAR (gde->calendar), mytm->tm_mday);
552     }
553 
554     /* Set the time of day. */
555     if (gde->flags & GNC_DATE_EDIT_24_HR)
556         qof_strftime (buffer, sizeof (buffer), "%H:%M", mytm);
557     else
558         qof_strftime (buffer, sizeof (buffer), "%I:%M %p", mytm);
559     gtk_entry_set_text(GTK_ENTRY(gde->time_entry), buffer);
560 
561     gnc_tm_free (mytm);
562 
563     g_signal_emit (gde, date_edit_signals [DATE_CHANGED], 0);
564     g_signal_emit (gde, date_edit_signals [TIME_CHANGED], 0);
565 }
566 
567 
568 /** Retrieve a property specific to this GncPeriodSelect object.  This is
569  *  nothing more than a dispatch function for routines that can be
570  *  called directly.  It has the nice feature of allowing a single
571  *  function call to retrieve multiple properties.
572  *
573  *  @internal
574  */
575 static void
gnc_date_edit_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)576 gnc_date_edit_get_property (GObject     *object,
577                             guint        prop_id,
578                             GValue      *value,
579                             GParamSpec  *pspec)
580 {
581     GNCDateEdit *date_edit = GNC_DATE_EDIT (object);
582 
583     switch (prop_id)
584     {
585     case PROP_TIME:
586         g_value_set_int64 (value, gnc_date_edit_get_date (date_edit));
587         break;
588     default:
589         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
590         break;
591     }
592 }
593 
594 
595 /** Set a property specific to this GncDateEdit object.  This is
596  *  nothing more than a dispatch function for routines that can be
597  *  called directly.  It has the nice feature of allowing a new widget
598  *  to be created with a varargs list specifying the properties,
599  *  instead of having to explicitly call each property function.
600  *
601  *  @internal
602  */
603 static void
gnc_date_edit_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)604 gnc_date_edit_set_property (GObject      *object,
605                             guint         prop_id,
606                             const GValue *value,
607                             GParamSpec   *pspec)
608 {
609     GNCDateEdit *date_edit = GNC_DATE_EDIT (object);
610 
611     switch (prop_id)
612     {
613     case PROP_TIME:
614         gnc_date_edit_set_time_internal (date_edit, g_value_get_int64(value));
615         break;
616     default:
617         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
618         break;
619     }
620 }
621 
622 static void
gnc_date_edit_class_init(GNCDateEditClass * klass)623 gnc_date_edit_class_init (GNCDateEditClass *klass)
624 {
625     GtkContainerClass *container_class = (GtkContainerClass *) klass;
626     GObjectClass *object_class = (GObjectClass *) klass;
627 
628     container_class->forall = gnc_date_edit_forall;
629     object_class->set_property = gnc_date_edit_set_property;
630     object_class->get_property = gnc_date_edit_get_property;
631     object_class->dispose = gnc_date_edit_dispose;
632     object_class->finalize = gnc_date_edit_finalize;
633 
634     parent_class = g_type_class_ref(GTK_TYPE_BOX);
635 
636     date_edit_signals [TIME_CHANGED] =
637         g_signal_new ("time_changed",
638                       G_TYPE_FROM_CLASS (object_class),
639                       G_SIGNAL_RUN_FIRST,
640                       G_STRUCT_OFFSET (GNCDateEditClass, time_changed),
641                       NULL, NULL,
642                       g_cclosure_marshal_VOID__VOID,
643                       G_TYPE_NONE, 0);
644 
645     date_edit_signals [DATE_CHANGED] =
646         g_signal_new ("date_changed",
647                       G_TYPE_FROM_CLASS (object_class),
648                       G_SIGNAL_RUN_FIRST,
649                       G_STRUCT_OFFSET (GNCDateEditClass, date_changed),
650                       NULL, NULL,
651                       g_cclosure_marshal_VOID__VOID,
652                       G_TYPE_NONE, 0);
653 
654     g_object_class_install_property(object_class,
655                                     PROP_TIME,
656                                     g_param_spec_int64("time",
657                                             "Date/time (seconds)",
658                                             "Date/time represented in seconds since Januari 31st, 1970",
659                                             G_MININT64,
660                                             G_MAXINT64,
661                                             0,
662                                             G_PARAM_READWRITE));
663 
664     klass->date_changed = NULL;
665     klass->time_changed = NULL;
666 }
667 
668 static void
gnc_date_edit_init(GNCDateEdit * gde)669 gnc_date_edit_init (GNCDateEdit *gde)
670 {
671     gtk_orientable_set_orientation (GTK_ORIENTABLE(gde), GTK_ORIENTATION_HORIZONTAL);
672 
673     // Set the name for this widget so it can be easily manipulated with css
674     gtk_widget_set_name (GTK_WIDGET(gde), "gnc-id-date-edit");
675 
676     gde->disposed = FALSE;
677     gde->popup_in_progress = FALSE;
678     gde->lower_hour = 7;
679     gde->upper_hour = 19;
680     gde->flags = GNC_DATE_EDIT_SHOW_TIME;
681     gde->in_selected_handler = FALSE;
682 }
683 
684 static void
gnc_date_edit_finalize(GObject * object)685 gnc_date_edit_finalize (GObject *object)
686 {
687 
688     g_return_if_fail (object != NULL);
689     g_return_if_fail (GNC_IS_DATE_EDIT (object));
690 
691     if (G_OBJECT_CLASS (parent_class)->finalize)
692         (* G_OBJECT_CLASS (parent_class)->finalize) (object);
693 }
694 
695 static void
gnc_date_edit_dispose(GObject * object)696 gnc_date_edit_dispose (GObject *object)
697 {
698     GNCDateEdit *gde;
699 
700     g_return_if_fail (object != NULL);
701     g_return_if_fail (GNC_IS_DATE_EDIT (object));
702 
703     gde = GNC_DATE_EDIT (object);
704 
705     if (gde->disposed)
706         return;
707 
708     gde->disposed = TRUE;
709 
710     /* Only explicitly destroy the toplevel elements */
711 
712     gtk_widget_destroy (GTK_WIDGET(gde->date_entry));
713     gde->date_entry = NULL;
714 
715     gtk_widget_destroy (GTK_WIDGET(gde->date_button));
716     gde->date_button = NULL;
717 
718     gtk_widget_destroy (GTK_WIDGET(gde->time_entry));
719     gde->time_entry = NULL;
720 
721     gtk_widget_destroy (GTK_WIDGET(gde->time_combo));
722     gde->time_combo = NULL;
723 
724     if (G_OBJECT_CLASS (parent_class)->dispose)
725         (* G_OBJECT_CLASS (parent_class)->dispose) (object);
726 }
727 
728 static void
gnc_date_edit_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)729 gnc_date_edit_forall (GtkContainer *container, gboolean include_internals,
730                       GtkCallback callback, gpointer callback_data)
731 {
732     g_return_if_fail (container != NULL);
733     g_return_if_fail (GNC_IS_DATE_EDIT (container));
734     g_return_if_fail (callback != NULL);
735 
736     /* Let GtkBox handle things only if the internal widgets need
737      * to be poked.  */
738     if (!include_internals)
739         return;
740 
741     if (!GTK_CONTAINER_CLASS (parent_class)->forall)
742         return;
743 
744     GTK_CONTAINER_CLASS (parent_class)->forall (container,
745             include_internals,
746             callback,
747             callback_data);
748 }
749 
750 /**
751  * gnc_date_edit_set_time:
752  * @gde: the GNCDateEdit widget
753  * @the_time: The time and date that should be set on the widget
754  *
755  * Changes the displayed date and time in the GNCDateEdit widget
756  * to be the one represented by @the_time.
757  */
758 void
gnc_date_edit_set_time(GNCDateEdit * gde,time64 the_time)759 gnc_date_edit_set_time (GNCDateEdit *gde, time64 the_time)
760 {
761     g_return_if_fail (gde != NULL);
762     g_return_if_fail (GNC_IS_DATE_EDIT (gde));
763 
764     /* If the_time is invalid, use the last valid time
765      * seen (or as a last resort, the current date). */
766     gde->initial_time = the_time;
767 
768     g_object_set (G_OBJECT (gde), "time", the_time, NULL);
769 }
770 
771 void
gnc_date_edit_set_gdate(GNCDateEdit * gde,const GDate * date)772 gnc_date_edit_set_gdate (GNCDateEdit *gde, const GDate *date)
773 {
774     struct tm mytm;
775     time64 t;
776 
777     g_return_if_fail(gde && GNC_IS_DATE_EDIT(gde) &&
778                      date && g_date_valid(date));
779     g_date_to_struct_tm(date, &mytm);
780     t = gnc_mktime(&mytm);
781     gnc_date_edit_set_time(gde, t);
782 }
783 
784 /**
785  * gnc_date_edit_set_popup_range:
786  * @gde: The GNCDateEdit widget
787  * @low_hour: low boundary for the time-range display popup.
788  * @up_hour:  upper boundary for the time-range display popup.
789  *
790  * Sets the range of times that will be provide by the time popup
791  * selectors.
792  */
793 void
gnc_date_edit_set_popup_range(GNCDateEdit * gde,int low_hour,int up_hour)794 gnc_date_edit_set_popup_range (GNCDateEdit *gde, int low_hour, int up_hour)
795 {
796     g_return_if_fail (gde != NULL);
797     g_return_if_fail (GNC_IS_DATE_EDIT (gde));
798 
799     gde->lower_hour = low_hour;
800     gde->upper_hour = up_hour;
801 
802     fill_time_combo(NULL, gde);
803 }
804 
805 /* This code should be kept in sync with src/register/datecell.c */
806 static int
date_accel_key_press(GtkWidget * widget,GdkEventKey * event,gpointer data)807 date_accel_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
808 {
809     GNCDateEdit *gde = data;
810     const char *string;
811     struct tm tm;
812 
813     string = gtk_entry_get_text (GTK_ENTRY (widget));
814 
815     tm = gnc_date_edit_get_date_internal (gde);
816 
817     if (!gnc_handle_date_accelerator (event, &tm, string))
818         return FALSE;
819 
820     gnc_date_edit_set_time (gde, gnc_mktime (&tm));
821 
822     g_signal_emit (G_OBJECT (gde), date_edit_signals [TIME_CHANGED], 0);
823     return TRUE;
824 }
825 
826 static gint
key_press_entry(GtkWidget * widget,GdkEventKey * event,gpointer data)827 key_press_entry (GtkWidget *widget, GdkEventKey *event, gpointer data)
828 {
829     if (!date_accel_key_press(widget, event, data))
830         return FALSE;
831 
832     g_signal_stop_emission_by_name (widget, "key-press-event");
833     return TRUE;
834 }
835 
836 static int
date_focus_out_event(GtkWidget * widget,GdkEventKey * event,gpointer data)837 date_focus_out_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
838 {
839     GNCDateEdit *gde = data;
840     struct tm tm;
841 
842     /* Get the date entered and attempt to use it. */
843     tm = gnc_date_edit_get_date_internal (gde);
844     gnc_date_edit_set_time (gde, gnc_mktime (&tm));
845 
846     /* Get the date again in case it was invalid the first time. */
847     tm = gnc_date_edit_get_date_internal (gde);
848 
849     g_signal_emit (gde, date_edit_signals [DATE_CHANGED], 0);
850     g_signal_emit (gde, date_edit_signals [TIME_CHANGED], 0);
851 
852     return FALSE;
853 }
854 
855 static void
create_children(GNCDateEdit * gde)856 create_children (GNCDateEdit *gde)
857 {
858     GtkWidget *frame;
859     GtkWidget *hbox;
860     GtkWidget *arrow;
861     GtkTreeStore *store;
862     GtkCellRenderer *cell;
863 
864     /* Create the text entry area. */
865     gde->date_entry  = gtk_entry_new ();
866     gtk_entry_set_width_chars (GTK_ENTRY (gde->date_entry), 11);
867     gtk_box_pack_start (GTK_BOX (gde), gde->date_entry, TRUE, TRUE, 0);
868     gtk_widget_show (GTK_WIDGET(gde->date_entry));
869     g_signal_connect (G_OBJECT (gde->date_entry), "key-press-event",
870                       G_CALLBACK (key_press_entry), gde);
871     g_signal_connect (G_OBJECT (gde->date_entry), "focus-out-event",
872                       G_CALLBACK (date_focus_out_event), gde);
873 
874     /* Create the popup button. */
875     gde->date_button = gtk_toggle_button_new ();
876     g_signal_connect (gde->date_button, "button-press-event",
877                       G_CALLBACK(gnc_date_edit_button_pressed), gde);
878     g_signal_connect (G_OBJECT (gde->date_button), "toggled",
879                       G_CALLBACK (gnc_date_edit_button_toggled), gde);
880     gtk_box_pack_start (GTK_BOX (gde), gde->date_button, FALSE, FALSE, 0);
881 
882     hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
883     gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
884     gtk_container_add (GTK_CONTAINER (gde->date_button), hbox);
885     gtk_widget_show (GTK_WIDGET(hbox));
886 
887     /* Calendar label, only shown if the date editor has a time field */
888     gde->cal_label = gtk_label_new (_("Calendar"));
889     gnc_label_set_alignment (gde->cal_label, 0.0, 0.5);
890     gtk_box_pack_start (GTK_BOX (hbox), gde->cal_label, TRUE, TRUE, 0);
891     if (gde->flags & GNC_DATE_EDIT_SHOW_TIME)
892         gtk_widget_show (GTK_WIDGET(gde->cal_label));
893 
894     /* Graphic for the popup button. */
895     arrow = gtk_image_new_from_icon_name ("pan-down-symbolic", GTK_ICON_SIZE_BUTTON);
896 
897     gtk_box_pack_start (GTK_BOX (hbox), arrow, TRUE, FALSE, 0);
898     gtk_widget_show (GTK_WIDGET(arrow));
899 
900     gtk_widget_show (GTK_WIDGET(gde->date_button));
901 
902     /* Time entry controls. */
903     gde->time_entry = gtk_entry_new ();
904     gtk_entry_set_max_length (GTK_ENTRY(gde->time_entry), 12);
905     gtk_widget_set_size_request (GTK_WIDGET(gde->time_entry), 88, -1);
906     gtk_box_pack_start (GTK_BOX (gde), gde->time_entry, TRUE, TRUE, 0);
907 
908     store = gtk_tree_store_new(1, G_TYPE_STRING);
909     gde->time_combo = GTK_WIDGET(gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)));
910     g_object_unref(store);
911     /* Create cell renderer. */
912     cell = gtk_cell_renderer_text_new();
913     /* Pack it to the combo box. */
914     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( gde->time_combo ), cell, TRUE );
915     /* Connect renderer to data source */
916     gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT( gde->time_combo ), cell, "text", 0, NULL );
917 
918     g_signal_connect (G_OBJECT (gde->time_combo), "changed",
919                       G_CALLBACK  (set_time), gde);
920 
921     gtk_box_pack_start (GTK_BOX (gde), gde->time_combo, FALSE, FALSE, 0);
922 
923     /* We do not create the popup menu with the hour range until we are
924      * realized, so that it uses the values that the user might supply in a
925      * future call to gnc_date_edit_set_popup_range
926      */
927     g_signal_connect (G_OBJECT (gde), "realize",
928                       G_CALLBACK  (fill_time_combo), gde);
929 
930     if (gde->flags & GNC_DATE_EDIT_SHOW_TIME)
931     {
932         gtk_widget_show (GTK_WIDGET(gde->time_entry));
933         gtk_widget_show (GTK_WIDGET(gde->time_combo));
934     }
935 
936     gde->cal_popup = gtk_window_new (GTK_WINDOW_POPUP);
937     gtk_widget_set_name (gde->cal_popup, "gnc-date-edit-popup-window");
938 
939     gtk_window_set_type_hint (GTK_WINDOW (gde->cal_popup),
940                               GDK_WINDOW_TYPE_HINT_COMBO);
941 
942     gtk_widget_set_events (GTK_WIDGET(gde->cal_popup),
943                            gtk_widget_get_events (GTK_WIDGET(gde->cal_popup)) |
944                            GDK_KEY_PRESS_MASK);
945 
946     g_signal_connect (gde->cal_popup, "delete-event",
947                       G_CALLBACK(delete_popup), gde);
948     g_signal_connect (gde->cal_popup, "key-press-event",
949                       G_CALLBACK(key_press_popup), gde);
950     g_signal_connect (gde->cal_popup, "button-press-event",
951                       G_CALLBACK(gnc_date_edit_button_pressed), gde);
952     g_signal_connect (gde->cal_popup, "button-release-event",
953                       G_CALLBACK(gnc_date_edit_button_released), gde);
954     gtk_window_set_resizable (GTK_WINDOW (gde->cal_popup), FALSE);
955     gtk_window_set_screen (GTK_WINDOW (gde->cal_popup),
956                            gtk_widget_get_screen (GTK_WIDGET (gde)));
957 
958     frame = gtk_frame_new (NULL);
959     gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
960     gtk_container_add (GTK_CONTAINER (gde->cal_popup), frame);
961     gtk_widget_show (GTK_WIDGET(frame));
962 
963     gde->calendar = gtk_calendar_new ();
964     gtk_calendar_set_display_options
965     (GTK_CALENDAR (gde->calendar),
966      (GTK_CALENDAR_SHOW_DAY_NAMES
967       | GTK_CALENDAR_SHOW_HEADING));
968     g_signal_connect (gde->calendar, "button-release-event",
969                       G_CALLBACK(gnc_date_edit_button_released), gde);
970     g_signal_connect (G_OBJECT (gde->calendar), "day-selected",
971 		      G_CALLBACK (day_selected), gde);
972     g_signal_connect (G_OBJECT (gde->calendar),
973                       "day-selected-double-click",
974                       G_CALLBACK  (day_selected_double_click), gde);
975     gtk_container_add (GTK_CONTAINER (frame), gde->calendar);
976     gtk_widget_show (GTK_WIDGET(gde->calendar));
977 }
978 
979 /**
980  * gnc_date_edit_new:
981  * @the_time: date and time to be displayed on the widget
982  * @show_time: whether time should be displayed
983  * @use_24_format: whether 24-hour format is desired for the time display.
984  *
985  * Creates a new GNCDateEdit widget which can be used to provide
986  * an easy to use way for entering dates and times.
987  *
988  * Returns a GNCDateEdit widget.
989  */
990 GtkWidget *
gnc_date_edit_new(time64 the_time,int show_time,int use_24_format)991 gnc_date_edit_new (time64 the_time, int show_time, int use_24_format)
992 {
993     return gnc_date_edit_new_flags
994            (the_time,
995             ((show_time ? GNC_DATE_EDIT_SHOW_TIME : 0)
996              | (use_24_format ? GNC_DATE_EDIT_24_HR : 0)));
997 }
998 
999 /*
1000  * Create a new GncDateEdit widget from a glade file.  The widget
1001  * generated is set to today's date, and will not show a time as part
1002  * of the date.  This function does not use any of the arguments
1003  * passed by glade.
1004  */
1005 GtkWidget *
gnc_date_edit_new_glade(gchar * widget_name,gchar * string1,gchar * string2,gint int1,gint int2)1006 gnc_date_edit_new_glade (gchar *widget_name,
1007                          gchar *string1, gchar *string2,
1008                          gint int1, gint int2)
1009 {
1010     GtkWidget *widget;
1011 
1012     /* None of the standard glade arguments are used. */
1013     widget = gnc_date_edit_new(time(NULL), FALSE, FALSE);
1014     gtk_widget_show(widget);
1015     return widget;
1016 }
1017 
1018 
1019 /**
1020  * gnc_date_edit_new_flags:
1021  * @the_time: The initial time for the date editor.
1022  * @flags: A bitmask of GNCDateEditFlags values.
1023  *
1024  * Creates a new GNCDateEdit widget with the specified flags.
1025  *
1026  * Return value: the newly-created date editor widget.
1027  **/
1028 GtkWidget *
gnc_date_edit_new_flags(time64 the_time,GNCDateEditFlags flags)1029 gnc_date_edit_new_flags (time64 the_time, GNCDateEditFlags flags)
1030 {
1031     GNCDateEdit *gde;
1032 
1033     gde = g_object_new (GNC_TYPE_DATE_EDIT, NULL, NULL);
1034 
1035     gde->flags = flags;
1036     gde->initial_time = -1;
1037     create_children (gde);
1038     gnc_date_edit_set_time (gde, the_time);
1039 
1040     return GTK_WIDGET (gde);
1041 }
1042 
1043 static struct tm
gnc_date_edit_get_date_internal(GNCDateEdit * gde)1044 gnc_date_edit_get_date_internal (GNCDateEdit *gde)
1045 {
1046     struct tm tm = {0};
1047     char *str;
1048     gchar *flags = NULL;
1049     gboolean date_was_valid;
1050 
1051     /* Assert, because we're just hosed if it's NULL */
1052     g_assert(gde != NULL);
1053     g_assert(GNC_IS_DATE_EDIT(gde));
1054 
1055     date_was_valid = qof_scan_date (gtk_entry_get_text (GTK_ENTRY (gde->date_entry)),
1056                                     &tm.tm_mday, &tm.tm_mon, &tm.tm_year);
1057 
1058     if (!date_was_valid)
1059     {
1060         /* Hm... no valid date. What should we do not? As a hacky workaround we
1061         revert to today's date. Alternatively we can return some value that
1062         signals that we don't get a valid date, but all callers of this
1063         function will have to check this. Alas, I'm too lazy to do this here. */
1064         gnc_tm_get_today_start(&tm);
1065     }
1066 
1067     tm.tm_mon--;
1068 
1069     tm.tm_year -= 1900;
1070 
1071     if (gde->flags & GNC_DATE_EDIT_SHOW_TIME)
1072     {
1073         char *tokp = NULL;
1074         gchar *temp;
1075 
1076         str = g_strdup (gtk_entry_get_text
1077                         (GTK_ENTRY (gde->time_entry)));
1078         temp = gnc_strtok_r (str, ": ", &tokp);
1079         if (temp)
1080         {
1081             tm.tm_hour = atoi (temp);
1082             temp = gnc_strtok_r (NULL, ": ", &tokp);
1083             if (temp)
1084             {
1085                 if (isdigit (*temp))
1086                 {
1087                     tm.tm_min = atoi (temp);
1088                     flags = gnc_strtok_r (NULL, ": ",
1089                                           &tokp);
1090                     if (flags && isdigit (*flags))
1091                     {
1092                         tm.tm_sec = atoi (flags);
1093                         flags = gnc_strtok_r (NULL,
1094                                               ": ",
1095                                               &tokp);
1096                     }
1097                 }
1098                 else
1099                     flags = temp;
1100             }
1101         }
1102 
1103         if (flags && (strcasecmp (flags, "PM") == 0))
1104         {
1105             if (tm.tm_hour < 12)
1106                 tm.tm_hour += 12;
1107         }
1108         g_free (str);
1109     }
1110     else
1111     {
1112         gnc_tm_set_day_start(&tm);
1113     }
1114 
1115     tm.tm_isdst = -1;
1116 
1117     return tm;
1118 }
1119 
1120 /**
1121  * gnc_date_edit_get_date:
1122  * @gde: The GNCDateEdit widget
1123  *
1124  * Returns the time entered in the GNCDateEdit widget
1125  */
1126 time64
gnc_date_edit_get_date(GNCDateEdit * gde)1127 gnc_date_edit_get_date (GNCDateEdit *gde)
1128 {
1129     struct tm tm;
1130 
1131     g_return_val_if_fail (gde != NULL, 0);
1132     g_return_val_if_fail (GNC_IS_DATE_EDIT (gde), 0);
1133 
1134     tm = gnc_date_edit_get_date_internal (gde);
1135 
1136     return gnc_mktime (&tm);
1137 }
1138 
1139 void
gnc_date_edit_get_gdate(GNCDateEdit * gde,GDate * date)1140 gnc_date_edit_get_gdate (GNCDateEdit *gde, GDate *date)
1141 {
1142     time64 t;
1143 
1144     g_return_if_fail (gde && date);
1145     g_return_if_fail (GNC_IS_DATE_EDIT (gde));
1146 
1147     t = gnc_date_edit_get_date(gde);
1148     g_date_clear (date, 1);
1149     gnc_gdate_set_time64 (date, t);
1150 }
1151 
1152 /**
1153  * gnc_date_edit_get_date_end:
1154  * @gde: The GNCDateEdit widget
1155  *
1156  * Returns the date entered in the GNCDateEdit widget,
1157  * but with the time adjusted to the end of the day.
1158  */
1159 time64
gnc_date_edit_get_date_end(GNCDateEdit * gde)1160 gnc_date_edit_get_date_end (GNCDateEdit *gde)
1161 {
1162     struct tm tm;
1163 
1164     g_return_val_if_fail (gde != NULL, 0);
1165     g_return_val_if_fail (GNC_IS_DATE_EDIT (gde), 0);
1166 
1167     tm = gnc_date_edit_get_date_internal (gde);
1168     gnc_tm_set_day_end(&tm);
1169 
1170     return gnc_mktime (&tm);
1171 }
1172 
1173 /**
1174  * gnc_date_edit_set_flags:
1175  * @gde: The date editor widget whose flags should be changed.
1176  * @flags: The new bitmask of GNCDateEditFlags values.
1177  *
1178  * Changes the display flags on an existing date editor widget.
1179  **/
1180 void
gnc_date_edit_set_flags(GNCDateEdit * gde,GNCDateEditFlags flags)1181 gnc_date_edit_set_flags (GNCDateEdit *gde, GNCDateEditFlags flags)
1182 {
1183     GNCDateEditFlags old_flags;
1184 
1185     g_return_if_fail (gde != NULL);
1186     g_return_if_fail (GNC_IS_DATE_EDIT (gde));
1187 
1188     old_flags = gde->flags;
1189     gde->flags = flags;
1190 
1191     if ((flags & GNC_DATE_EDIT_SHOW_TIME) !=
1192             (old_flags & GNC_DATE_EDIT_SHOW_TIME))
1193     {
1194         if (flags & GNC_DATE_EDIT_SHOW_TIME)
1195         {
1196             gtk_widget_show (gde->cal_label);
1197             gtk_widget_show (gde->time_entry);
1198             gtk_widget_show (gde->time_combo);
1199         }
1200         else
1201         {
1202             gtk_widget_hide (gde->cal_label);
1203             gtk_widget_hide (gde->time_entry);
1204             gtk_widget_hide (gde->time_combo);
1205         }
1206     }
1207 
1208     if ((flags & GNC_DATE_EDIT_24_HR) != (old_flags & GNC_DATE_EDIT_24_HR))
1209         /* This will destroy the old menu properly */
1210         fill_time_combo (NULL, gde);
1211 
1212 }
1213 
1214 /**
1215  * gnc_date_edit_get_flags:
1216  * @gde: The date editor whose flags should be queried.
1217  *
1218  * Queries the display flags on a date editor widget.
1219  *
1220  * Return value: The current display flags for the given date editor widget.
1221  **/
1222 int
gnc_date_edit_get_flags(GNCDateEdit * gde)1223 gnc_date_edit_get_flags (GNCDateEdit *gde)
1224 {
1225     g_return_val_if_fail (gde != NULL, 0);
1226     g_return_val_if_fail (GNC_IS_DATE_EDIT (gde), 0);
1227 
1228     return gde->flags;
1229 }
1230 
1231 /**
1232  * gnc_date_set_activates_default:
1233  * @gde: The date editor to modify
1234  * @state: The new state for this widget.
1235  *
1236  * Extracts the editable field from a GNCDateEdit widget, and sets it
1237  * up so that pressing the Enter key in this field as the same as
1238  * clicking the button that has the default.
1239  **/
1240 void
gnc_date_activates_default(GNCDateEdit * gde,gboolean state)1241 gnc_date_activates_default (GNCDateEdit *gde, gboolean state)
1242 {
1243     if (!gde)
1244         return;
1245 
1246     gtk_entry_set_activates_default(GTK_ENTRY(gde->date_entry), state);
1247 }
1248 
1249 /**
1250  * gnc_date_grab_focus:
1251  * @gde: The date editor to modify
1252  * @state: The new state for this widget.
1253  *
1254  * Sets the focus to the Editable field.
1255  **/
1256 void
gnc_date_grab_focus(GNCDateEdit * gde)1257 gnc_date_grab_focus (GNCDateEdit *gde)
1258 {
1259     if (!gde)
1260         return;
1261 
1262     gtk_widget_grab_focus (gde->date_entry);
1263 }
1264 /** Sets the editable field from a GNCDateEdit widget as the target
1265  *  for the specified label's access key.
1266  *
1267  *  @param gde The date editor to set as the target.
1268  *
1269  *  @param label The label whose access key should set focus to this
1270  *  widget. */
1271 void
gnc_date_make_mnemonic_target(GNCDateEdit * gde,GtkWidget * label)1272 gnc_date_make_mnemonic_target (GNCDateEdit *gde, GtkWidget *label)
1273 {
1274     if (!gde)
1275         return;
1276 
1277     gtk_label_set_mnemonic_widget (GTK_LABEL(label), gde->date_entry);
1278 }
1279 
1280 
1281