/* * Copyright (C) 2012 - Juan Ferrer Toribio * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "vn-date-chooser.h" #include #define GUI_FILE _GUI_DIR"/date-chooser.glade" #define DATE_SIZE 50 /** * SECTION:vn-date-chooser * @Short_description: an embedded popup date selector * @Title: VnDateChooser * @See_also: #VnField * @Image: date-chooser.png * * An embedded button and text field that pops-up a #GtkCalendar. */ G_DEFINE_TYPE (VnDateChooser, vn_date_chooser, VN_TYPE_FIELD); /** * vn_date_chooser_new: * * Creates a new #VnDateChooser * * Return value: a #VnDateChooser **/ VnField * vn_date_chooser_new () { return VN_FIELD (g_object_new (VN_TYPE_DATE_CHOOSER, NULL)); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Private static void vn_date_chooser_hide_popup (VnDateChooser * obj) { if (obj->device) { gdk_device_ungrab (obj->device, GDK_CURRENT_TIME); gtk_device_grab_remove (obj->popup, obj->device); } gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (obj->button), FALSE); gtk_widget_hide (obj->popup); } static void vn_date_chooser_changed (VnDateChooser * obj) { GDateTime * datetime = obj->datetime; if (datetime) { gchar * str = g_date_time_format (datetime, obj->format); gtk_label_set_text (obj->label, str); g_free (str); } else gtk_label_set_text (obj->label, ""); } static void vn_date_chooser_set_value (VnDateChooser * obj, const GValue * value) { GDateTime * datetime = NULL; if (gvn_value_is_null (value) || G_VALUE_TYPE (value) == G_TYPE_DATE_TIME) datetime = g_value_get_boxed (value); else { GValue new_value = G_VALUE_INIT; g_value_init (&new_value, G_TYPE_DATE_TIME); g_value_transform (value, &new_value); datetime = g_value_get_boxed (&new_value); } if (datetime) { if (gvn_value_is_null (datetime)) obj->datetime = datetime; else obj->datetime = g_date_time_ref (datetime); } else if (obj->datetime) { g_date_time_unref (obj->datetime); obj->datetime = NULL; } vn_date_chooser_changed (obj); } static GDateTime * vn_date_chooser_get_datetime (VnDateChooser * obj) { guint year, month, day, hour = 0, minute = 0; gdouble second = 0.0; gtk_calendar_get_date (obj->calendar, &year, &month, &day); month++; hour = (guint) gtk_adjustment_get_value (obj->hour); minute = (guint) gtk_adjustment_get_value (obj->minute); second = gtk_adjustment_get_value (obj->second); return g_date_time_new_local (year, month, day, hour, minute, second); } void vn_date_chooser_on_day_selected (GtkCalendar * calendar, VnDateChooser * obj) { GValue value = G_VALUE_INIT; GDateTime * datetime = vn_date_chooser_get_datetime (obj); if (!obj->datetime || (obj->datetime && g_date_time_compare (datetime, obj->datetime))) { obj->datetime = datetime; g_value_init (&value, G_TYPE_DATE_TIME); g_value_set_boxed (&value, obj->datetime); } else { g_value_init (&value, GVN_TYPE_NULL); if (obj->datetime) g_date_time_unref (obj->datetime); obj->datetime = NULL; } VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); g_value_unset (&value); vn_date_chooser_hide_popup (obj); vn_date_chooser_changed (obj); } void vn_date_chooser_on_ok_clicked (GtkButton * button, VnDateChooser * obj) { GDateTime * datetime = vn_date_chooser_get_datetime (obj); if (datetime) { GValue value = G_VALUE_INIT; if (obj->datetime) g_date_time_unref (obj->datetime); obj->datetime = datetime; g_value_init (&value, G_TYPE_DATE_TIME); g_value_set_boxed (&value, datetime); VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); g_value_unset (&value); } vn_date_chooser_hide_popup (obj); vn_date_chooser_changed (obj); } void vn_date_chooser_on_clear_clicked (GtkButton * button, VnDateChooser * obj) { GValue value = G_VALUE_INIT; g_value_init (&value, GVN_TYPE_NULL); if (obj->datetime) g_date_time_unref (obj->datetime); obj->datetime = NULL; VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); g_value_unset (&value); vn_date_chooser_hide_popup (obj); vn_date_chooser_changed (obj); gtk_adjustment_set_value (obj->hour, 0); gtk_adjustment_set_value (obj->minute, 0); gtk_adjustment_set_value (obj->second, 0); } void vn_date_chooser_on_now_clicked (GtkButton * button, VnDateChooser * obj) { GValue value = G_VALUE_INIT; if (obj->datetime) g_date_time_unref (obj->datetime); obj->datetime = g_date_time_new_now_local (); g_value_init (&value, G_TYPE_DATE_TIME); g_value_set_boxed (&value, obj->datetime); VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); g_value_unset (&value); vn_date_chooser_hide_popup (obj); vn_date_chooser_changed (obj); } static gboolean vn_date_chooser_on_button_press (GtkWidget * widget, GdkEventButton * event, VnDateChooser * obj) { gint x, y; GtkAllocation allocation; gdk_window_get_origin (gtk_widget_get_window (obj->popup), &x, &y); gtk_widget_get_allocation (obj->popup, &allocation); if (!( event->x_root >= x && event->x_root <= x + allocation.width && event->y_root >= y && event->y_root <= y + allocation.height)) { vn_date_chooser_hide_popup (obj); return TRUE; } return FALSE; } static gboolean vn_date_chooser_on_key_press (GtkWidget * widget, GdkEventKey * event, VnDateChooser * obj) { if (event->keyval == GDK_KEY_Escape) { vn_date_chooser_hide_popup (obj); return TRUE; } return FALSE; } static void vn_date_chooser_on_toggled (GtkToggleButton * button, VnDateChooser * obj) { if (gtk_toggle_button_get_active (button)) { gint x, y; GdkWindow * window; GdkScreen * screen; GtkRequisition req; GdkRectangle monitor; GtkAllocation allocation; GtkWidget * widget = GTK_WIDGET (button); // Set the date/time on the calendar if (obj->datetime) { gtk_calendar_select_month (obj->calendar ,g_date_time_get_month (obj->datetime) - 1 ,g_date_time_get_year (obj->datetime) ); gtk_calendar_select_day (obj->calendar, g_date_time_get_day_of_month (obj->datetime)); gtk_adjustment_set_value (obj->hour, g_date_time_get_hour (obj->datetime)); gtk_adjustment_set_value (obj->minute, g_date_time_get_minute (obj->datetime)); gtk_adjustment_set_value (obj->second, g_date_time_get_second (obj->datetime)); } else gtk_calendar_select_day (obj->calendar, 0); // Set visibility if (!obj->show_date) gtk_widget_hide (GTK_WIDGET (obj->calendar)); if (!obj->show_time) gtk_widget_hide (obj->time); gtk_widget_set_no_show_all (GTK_WIDGET (obj->calendar), !obj->show_date); gtk_widget_set_no_show_all (obj->time, !obj->show_time); gtk_widget_get_preferred_width (obj->box, &x, NULL); gtk_widget_get_preferred_height (obj->box, &y, NULL); gtk_window_resize (GTK_WINDOW (obj->popup), x, y); // Setting the position of the popup window = gtk_widget_get_window (widget); gtk_widget_get_allocation (widget, &allocation); gdk_window_get_origin (window, &x, &y); x += allocation.x; y += allocation.y; screen = gtk_widget_get_screen (widget); gdk_screen_get_monitor_geometry (screen, gdk_screen_get_monitor_at_point (screen, x, y), &monitor); gtk_widget_get_preferred_size (obj->popup, &req, NULL); if (y - monitor.y > monitor.height) y = monitor.y + monitor.height - req.height; else if ((y - monitor.y) + req.height + allocation.height > monitor.height) y -= req.height; else y += allocation.height; if ((x + allocation.width) - monitor.x > monitor.width) x = monitor.x + monitor.width - req.width; else if ((x - monitor.x) + req.width > monitor.width) x -= req.width - allocation.width; gtk_window_set_screen (GTK_WINDOW (obj->popup), screen); gtk_window_move (GTK_WINDOW (obj->popup), x, y); gtk_widget_show_all (obj->popup); // Grabbing the focus on the popup window obj->device = gtk_get_current_event_device (); if (obj->device && gdk_device_get_source (obj->device) == GDK_SOURCE_KEYBOARD) obj->device = gdk_device_get_associated_device (obj->device); if (!obj->device) { GList * devices; GdkDisplay * display; GdkDeviceManager * device_manager; display = gtk_widget_get_display (GTK_WIDGET (obj->popup)); device_manager = gdk_display_get_device_manager (display); devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER); obj->device = devices->data; g_list_free (devices); } gtk_device_grab_add (obj->popup, obj->device, TRUE); gdk_device_grab (obj->device ,gtk_widget_get_window (obj->popup) ,GDK_OWNERSHIP_WINDOW, TRUE ,GDK_BUTTON_PRESS_MASK, NULL ,GDK_CURRENT_TIME ); } else vn_date_chooser_hide_popup (obj); } gboolean vn_date_chooser_on_spin_output (GtkSpinButton * spin, VnDateChooser * obj) { gchar * format; GtkAdjustment * adj = gtk_spin_button_get_adjustment (spin); format = g_strdup_printf ("%02d", (guint) gtk_adjustment_get_value (adj)); gtk_entry_set_text (GTK_ENTRY (spin), format); g_free (format); return TRUE; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties enum { PROP_FORMAT = 1 ,PROP_SHOW_TIME ,PROP_SHOW_DATE }; static void vn_date_chooser_set_property (VnDateChooser * obj, guint id, const GValue * value, GParamSpec * pspec) { switch (id) { case PROP_FORMAT: g_free (obj->format); obj->format = g_value_dup_string (value); break; case PROP_SHOW_TIME: obj->show_time = g_value_get_boolean (value); break; case PROP_SHOW_DATE: obj->show_date = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); } } static void vn_date_chooser_get_property (VnDateChooser * obj, guint id, GValue * value, GParamSpec * pspec) { switch (id) { case PROP_FORMAT: g_value_set_string (value, obj->format); break; case PROP_SHOW_TIME: g_value_set_boolean (value, obj->show_time); break; case PROP_SHOW_DATE: g_value_set_boolean (value, obj->show_date); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); } } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Class static void vn_date_chooser_init (VnDateChooser * obj) { GtkBuilder * builder; obj->show_time = FALSE; obj->show_date = TRUE; obj->format = NULL; obj->popup = NULL; obj->device = NULL; obj->datetime = NULL; obj->button = gtk_toggle_button_new (); gtk_widget_set_tooltip_text (GTK_WIDGET (obj->button), _("Change date")); g_signal_connect (obj->button, "toggled", G_CALLBACK (vn_date_chooser_on_toggled), obj); VN_FIELD (obj)->field = obj->button; gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->button)); g_object_set (obj->button, "relief", GTK_RELIEF_HALF, NULL); obj->label = GTK_LABEL (gtk_label_new (NULL)); gtk_misc_set_alignment (GTK_MISC (obj->label), 0, 0.5); gtk_container_add (GTK_CONTAINER (obj->button), GTK_WIDGET (obj->label)); obj->popup = gtk_window_new (GTK_WINDOW_POPUP); g_object_connect (obj->popup ,"signal::button-press-event", vn_date_chooser_on_button_press, obj ,"signal::key-press-event", vn_date_chooser_on_key_press, obj ,NULL ); builder = gtk_builder_new_from_file (GUI_FILE); gtk_builder_connect_signals (builder, obj); obj->calendar = GTK_CALENDAR (gtk_builder_get_object (builder, "calendar")); obj->hour = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "hour")); obj->minute = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "minute")); obj->second = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "second")); obj->time = GTK_WIDGET (gtk_builder_get_object (builder, "time")); obj->box = GTK_WIDGET (gtk_builder_get_object (builder, "box")); gtk_container_add (GTK_CONTAINER (obj->popup), obj->box); } static void vn_date_chooser_finalize (VnDateChooser * obj) { if (obj->datetime) g_date_time_unref (obj->datetime); g_free (obj->format); gtk_widget_destroy (obj->popup); G_OBJECT_CLASS (vn_date_chooser_parent_class)->finalize (G_OBJECT (obj)); } static void vn_date_chooser_class_init (VnDateChooserClass * klass) { GObjectClass * k = G_OBJECT_CLASS (klass); k->finalize = (GObjectFinalizeFunc) vn_date_chooser_finalize; k->set_property = (GObjectSetPropertyFunc) vn_date_chooser_set_property; k->get_property = (GObjectGetPropertyFunc) vn_date_chooser_get_property; VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_date_chooser_set_value; g_object_class_install_property (k, PROP_FORMAT, g_param_spec_string ("format" ,_("Format") ,_("The date format string describing the order of the elements.") ,C_("Default date format string", "%a, %d %b %Y") ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE )); g_object_class_install_property (k, PROP_SHOW_TIME, g_param_spec_boolean ("show-time" ,_("Show time") ,_("Whether to show the hour, minute and second fields to set" " the time of the day in the popup.") ,FALSE ,G_PARAM_READWRITE )); g_object_class_install_property (k, PROP_SHOW_DATE, g_param_spec_boolean ("show-date" ,_("Show date") ,_("Whether to show the calendar to set the date in the popup.") ,TRUE ,G_PARAM_READWRITE )); }