502 lines
14 KiB
C
502 lines
14 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>
|
|
|
|
#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 * self)
|
|
{
|
|
if (self->device)
|
|
{
|
|
gdk_device_ungrab (self->device, GDK_CURRENT_TIME);
|
|
gtk_device_grab_remove (self->popup, self->device);
|
|
}
|
|
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->button), FALSE);
|
|
gtk_widget_hide (self->popup);
|
|
}
|
|
|
|
static void vn_date_chooser_changed (VnDateChooser * self)
|
|
{
|
|
GDateTime * datetime = self->datetime;
|
|
|
|
if (datetime)
|
|
{
|
|
gchar * str = g_date_time_format (datetime, self->format);
|
|
gtk_label_set_text (self->label, str);
|
|
g_free (str);
|
|
}
|
|
else
|
|
gtk_label_set_text (self->label, "");
|
|
}
|
|
|
|
static void vn_date_chooser_set_value (VnDateChooser * self, 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))
|
|
self->datetime = datetime;
|
|
else
|
|
self->datetime = g_date_time_ref (datetime);
|
|
}
|
|
else if (self->datetime)
|
|
{
|
|
g_date_time_unref (self->datetime);
|
|
self->datetime = NULL;
|
|
}
|
|
|
|
vn_date_chooser_changed (self);
|
|
}
|
|
|
|
static GDateTime * vn_date_chooser_get_datetime (VnDateChooser * self)
|
|
{
|
|
guint year, month, day, hour = 0, minute = 0;
|
|
gdouble second = 0.0;
|
|
|
|
gtk_calendar_get_date (self->calendar, &year, &month, &day);
|
|
month++;
|
|
|
|
hour = (guint) gtk_adjustment_get_value (self->hour);
|
|
minute = (guint) gtk_adjustment_get_value (self->minute);
|
|
second = gtk_adjustment_get_value (self->second);
|
|
|
|
return g_date_time_new_local (year, month, day, hour, minute, second);
|
|
}
|
|
|
|
void vn_date_chooser_on_day_selected (GtkCalendar * calendar, VnDateChooser * self)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
GDateTime * datetime = vn_date_chooser_get_datetime (self);
|
|
|
|
if (!self->datetime
|
|
|| (self->datetime && g_date_time_compare (datetime, self->datetime)))
|
|
{
|
|
self->datetime = datetime;
|
|
g_value_init (&value, G_TYPE_DATE_TIME);
|
|
g_value_set_boxed (&value, self->datetime);
|
|
}
|
|
else
|
|
{
|
|
g_value_init (&value, GVN_TYPE_NULL);
|
|
|
|
if (self->datetime)
|
|
g_date_time_unref (self->datetime);
|
|
|
|
self->datetime = NULL;
|
|
}
|
|
|
|
VN_FIELD_GET_CLASS (self)->value_changed (VN_FIELD (self), &value);
|
|
g_value_unset (&value);
|
|
|
|
vn_date_chooser_hide_popup (self);
|
|
vn_date_chooser_changed (self);
|
|
}
|
|
|
|
void vn_date_chooser_on_ok_clicked (GtkButton * button, VnDateChooser * self)
|
|
{
|
|
GDateTime * datetime = vn_date_chooser_get_datetime (self);
|
|
|
|
if (datetime)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
|
|
if (self->datetime)
|
|
g_date_time_unref (self->datetime);
|
|
|
|
self->datetime = datetime;
|
|
g_value_init (&value, G_TYPE_DATE_TIME);
|
|
g_value_set_boxed (&value, datetime);
|
|
VN_FIELD_GET_CLASS (self)->value_changed (VN_FIELD (self), &value);
|
|
g_value_unset (&value);
|
|
}
|
|
|
|
vn_date_chooser_hide_popup (self);
|
|
vn_date_chooser_changed (self);
|
|
}
|
|
|
|
void vn_date_chooser_on_clear_clicked (GtkButton * button, VnDateChooser * self)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
g_value_init (&value, GVN_TYPE_NULL);
|
|
|
|
if (self->datetime)
|
|
g_date_time_unref (self->datetime);
|
|
|
|
self->datetime = NULL;
|
|
VN_FIELD_GET_CLASS (self)->value_changed (VN_FIELD (self), &value);
|
|
g_value_unset (&value);
|
|
vn_date_chooser_hide_popup (self);
|
|
vn_date_chooser_changed (self);
|
|
|
|
gtk_adjustment_set_value (self->hour, 0);
|
|
gtk_adjustment_set_value (self->minute, 0);
|
|
gtk_adjustment_set_value (self->second, 0);
|
|
}
|
|
|
|
void vn_date_chooser_on_now_clicked (GtkButton * button, VnDateChooser * self)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
|
|
if (self->datetime)
|
|
g_date_time_unref (self->datetime);
|
|
|
|
self->datetime = g_date_time_new_now_local ();
|
|
g_value_init (&value, G_TYPE_DATE_TIME);
|
|
g_value_set_boxed (&value, self->datetime);
|
|
VN_FIELD_GET_CLASS (self)->value_changed (VN_FIELD (self), &value);
|
|
g_value_unset (&value);
|
|
vn_date_chooser_hide_popup (self);
|
|
vn_date_chooser_changed (self);
|
|
}
|
|
|
|
static gboolean vn_date_chooser_on_button_press (GtkWidget * widget,
|
|
GdkEventButton * event, VnDateChooser * self)
|
|
{
|
|
gint x, y;
|
|
GtkAllocation allocation;
|
|
|
|
gdk_window_get_origin (gtk_widget_get_window (self->popup), &x, &y);
|
|
gtk_widget_get_allocation (self->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 (self);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean vn_date_chooser_on_key_press (GtkWidget * widget,
|
|
GdkEventKey * event, VnDateChooser * self)
|
|
{
|
|
if (event->keyval == GDK_KEY_Escape)
|
|
{
|
|
vn_date_chooser_hide_popup (self);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void vn_date_chooser_on_toggled (GtkToggleButton * button, VnDateChooser * self)
|
|
{
|
|
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 (self->datetime)
|
|
{
|
|
gtk_calendar_select_month (self->calendar
|
|
,g_date_time_get_month (self->datetime) - 1
|
|
,g_date_time_get_year (self->datetime)
|
|
);
|
|
gtk_calendar_select_day (self->calendar,
|
|
g_date_time_get_day_of_month (self->datetime));
|
|
|
|
gtk_adjustment_set_value (self->hour,
|
|
g_date_time_get_hour (self->datetime));
|
|
gtk_adjustment_set_value (self->minute,
|
|
g_date_time_get_minute (self->datetime));
|
|
gtk_adjustment_set_value (self->second,
|
|
g_date_time_get_second (self->datetime));
|
|
}
|
|
else
|
|
gtk_calendar_select_day (self->calendar, 0);
|
|
|
|
// Set visibility
|
|
|
|
if (!self->show_date)
|
|
gtk_widget_hide (GTK_WIDGET (self->calendar));
|
|
|
|
if (!self->show_time)
|
|
gtk_widget_hide (self->time);
|
|
|
|
gtk_widget_set_no_show_all (GTK_WIDGET (self->calendar), !self->show_date);
|
|
gtk_widget_set_no_show_all (self->time, !self->show_time);
|
|
|
|
gtk_widget_get_preferred_width (self->box, &x, NULL);
|
|
gtk_widget_get_preferred_height (self->box, &y, NULL);
|
|
|
|
gtk_window_resize (GTK_WINDOW (self->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 (self->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 (self->popup), screen);
|
|
gtk_window_move (GTK_WINDOW (self->popup), x, y);
|
|
gtk_widget_show_all (self->popup);
|
|
|
|
// Grabbing the focus on the popup window
|
|
|
|
self->device = gtk_get_current_event_device ();
|
|
|
|
if (self->device && gdk_device_get_source (self->device) == GDK_SOURCE_KEYBOARD)
|
|
self->device = gdk_device_get_associated_device (self->device);
|
|
|
|
if (!self->device)
|
|
{
|
|
GList * devices;
|
|
GdkDisplay * display;
|
|
GdkDeviceManager * device_manager;
|
|
|
|
display = gtk_widget_get_display (GTK_WIDGET (self->popup));
|
|
device_manager = gdk_display_get_device_manager (display);
|
|
|
|
devices = gdk_device_manager_list_devices (device_manager,
|
|
GDK_DEVICE_TYPE_MASTER);
|
|
self->device = devices->data;
|
|
g_list_free (devices);
|
|
}
|
|
|
|
gtk_device_grab_add (self->popup, self->device, TRUE);
|
|
gdk_device_grab (self->device
|
|
,gtk_widget_get_window (self->popup)
|
|
,GDK_OWNERSHIP_WINDOW, TRUE
|
|
,GDK_BUTTON_PRESS_MASK, NULL
|
|
,GDK_CURRENT_TIME
|
|
);
|
|
}
|
|
else
|
|
vn_date_chooser_hide_popup (self);
|
|
}
|
|
|
|
gboolean vn_date_chooser_on_spin_output (GtkSpinButton * spin, VnDateChooser * self)
|
|
{
|
|
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 * self, guint id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
switch (id)
|
|
{
|
|
case PROP_FORMAT:
|
|
g_free (self->format);
|
|
self->format = g_value_dup_string (value);
|
|
break;
|
|
case PROP_SHOW_TIME:
|
|
self->show_time = g_value_get_boolean (value);
|
|
break;
|
|
case PROP_SHOW_DATE:
|
|
self->show_date = g_value_get_boolean (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, id, pspec);
|
|
}
|
|
}
|
|
|
|
static void vn_date_chooser_get_property (VnDateChooser * self, guint id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
switch (id)
|
|
{
|
|
case PROP_FORMAT:
|
|
g_value_set_string (value, self->format);
|
|
break;
|
|
case PROP_SHOW_TIME:
|
|
g_value_set_boolean (value, self->show_time);
|
|
break;
|
|
case PROP_SHOW_DATE:
|
|
g_value_set_boolean (value, self->show_date);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, id, pspec);
|
|
}
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class
|
|
|
|
static void vn_date_chooser_init (VnDateChooser * self)
|
|
{
|
|
GtkBuilder * builder;
|
|
|
|
self->show_time = FALSE;
|
|
self->show_date = TRUE;
|
|
self->format = NULL;
|
|
self->popup = NULL;
|
|
self->device = NULL;
|
|
self->datetime = NULL;
|
|
|
|
self->button = gtk_toggle_button_new ();
|
|
gtk_widget_set_tooltip_text (GTK_WIDGET (self->button), _("Change date"));
|
|
g_signal_connect (self->button, "toggled",
|
|
G_CALLBACK (vn_date_chooser_on_toggled), self);
|
|
gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->button));
|
|
g_object_set (self->button, "relief", GTK_RELIEF_HALF, NULL);
|
|
|
|
self->label = GTK_LABEL (gtk_label_new (NULL));
|
|
gtk_misc_set_alignment (GTK_MISC (self->label), 0, 0.5);
|
|
gtk_container_add (GTK_CONTAINER (self->button), GTK_WIDGET (self->label));
|
|
|
|
self->popup = gtk_window_new (GTK_WINDOW_POPUP);
|
|
g_object_connect (self->popup
|
|
,"signal::button-press-event", vn_date_chooser_on_button_press, self
|
|
,"signal::key-press-event", vn_date_chooser_on_key_press, self
|
|
,NULL
|
|
);
|
|
|
|
builder = gtk_builder_new_from_file (GUI_FILE);
|
|
gtk_builder_connect_signals (builder, self);
|
|
|
|
self->calendar = GTK_CALENDAR (gtk_builder_get_object (builder, "calendar"));
|
|
self->hour = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "hour"));
|
|
self->minute = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "minute"));
|
|
self->second = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "second"));
|
|
self->time = GTK_WIDGET (gtk_builder_get_object (builder, "time"));
|
|
|
|
self->box = GTK_WIDGET (gtk_builder_get_object (builder, "box"));
|
|
gtk_container_add (GTK_CONTAINER (self->popup), self->box);
|
|
|
|
VN_FIELD_GET_CLASS (self)->set_widget (VN_FIELD (self),
|
|
GTK_WIDGET (self->button));
|
|
}
|
|
|
|
static void vn_date_chooser_finalize (VnDateChooser * self)
|
|
{
|
|
if (self->datetime)
|
|
g_date_time_unref (self->datetime);
|
|
|
|
g_free (self->format);
|
|
gtk_widget_destroy (self->popup);
|
|
G_OBJECT_CLASS (vn_date_chooser_parent_class)->finalize (G_OBJECT (self));
|
|
}
|
|
|
|
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
|
|
));
|
|
}
|