318 lines
8.5 KiB
C
318 lines
8.5 KiB
C
|
/*
|
||
|
* 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 <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "vn-date-chooser.h"
|
||
|
#include <db/db.h>
|
||
|
|
||
|
/**
|
||
|
* 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;
|
||
|
}
|