/* * 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 /** * 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) { GDate * date = &obj->date; if (g_date_valid (date)) { gchar * str; GDateWeekday wday; GDateMonth month; month = g_date_get_month (date); wday = g_date_get_weekday (date); str = g_strdup_printf (_("%s, %u %s %u") ,GVN_ABR_WDAY[wday] ,g_date_get_day (date) ,GVN_ABR_MONTH[month] ,g_date_get_year (date) ); 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) { GDate * date = NULL; if (G_VALUE_TYPE (value) != G_TYPE_DATE) { GValue new_value = {0}; g_value_init (&new_value, G_TYPE_DATE); g_value_transform (value, &new_value); date = g_value_get_boxed (&new_value); } else if (!gvn_value_is_null (value)) date = g_value_get_boxed (value); if (date) obj->date = *date; else g_date_clear (&obj->date, 1); vn_date_chooser_changed (obj); } static void vn_date_chooser_on_day_selected (GtkCalendar * calendar, VnDateChooser * obj) { guint year; guint month; guint day; GDate new_date; GValue value = {0}; gtk_calendar_get_date (calendar, &year, &month, &day); month++; g_date_clear (&new_date, 1); g_date_set_dmy (&new_date, day, month, year); if (!g_date_valid (&obj->date) || g_date_compare (&new_date, &obj->date)) { g_date_set_julian (&obj->date, g_date_get_julian (&new_date)); g_value_init (&value, G_TYPE_DATE); g_value_set_boxed (&value, &obj->date); } else { g_value_init (&value, GVN_TYPE_NULL); g_date_clear (&obj->date, 1); } 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_buton_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 on the calendar if (g_date_valid (&obj->date)) { gtk_calendar_select_month (obj->calendar ,g_date_get_month (&obj->date) - 1 ,g_date_get_year (&obj->date) ); gtk_calendar_select_day (obj->calendar, g_date_get_day (&obj->date)); } else gtk_calendar_select_day (obj->calendar, 0); // 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_show (GTK_WIDGET (obj->calendar)); 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); // Graving 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_widget_grab_focus (obj->popup); 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); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Class static void vn_date_chooser_init (VnDateChooser * obj) { GtkWidget * image; obj->popup = NULL; obj->device = NULL; g_date_clear (&obj->date, 1); obj->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8); gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->box)); VN_FIELD (obj)->field = obj->box; 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); gtk_box_pack_start (GTK_BOX (obj->box), GTK_WIDGET (obj->button), FALSE, FALSE, 0); image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON); gtk_button_set_image (GTK_BUTTON (obj->button), image); obj->label = GTK_LABEL (gtk_label_new (NULL)); gtk_misc_set_alignment (GTK_MISC (obj->label), 0, 0.5); gtk_box_pack_end (GTK_BOX (obj->box), GTK_WIDGET (obj->label), TRUE, TRUE, 0); obj->popup = gtk_window_new (GTK_WINDOW_POPUP); g_object_connect (obj->popup ,"signal::button-press-event", vn_date_chooser_on_buton_press, obj ,"signal::key-press-event", vn_date_chooser_on_key_press, obj ,NULL ); obj->calendar = GTK_CALENDAR (gtk_calendar_new ()); g_signal_connect (obj->calendar, "day-selected-double-click", G_CALLBACK (vn_date_chooser_on_day_selected), obj); gtk_container_add (GTK_CONTAINER (obj->popup), GTK_WIDGET (obj->calendar)); } static void vn_date_chooser_finalize (VnDateChooser * obj) { 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) { G_OBJECT_CLASS (klass)->finalize = (GObjectFinalizeFunc) vn_date_chooser_finalize; VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_date_chooser_set_value; }