This repository has been archived on 2024-07-15. You can view files and clone it, but cannot push or open issues or pull requests.
hedera/vn/field/vn-date-chooser.c

512 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 * 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;
if (obj->show_date)
{
gtk_calendar_get_date (obj->calendar, &year, &month, &day);
month++;
}
if (obj->show_time)
{
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 on the calendar
if (obj->datetime)
{
if (obj->show_date)
{
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));
}
if (obj->show_time)
{
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));
gtk_widget_set_no_show_all (GTK_WIDGET (obj->time), obj->show_time);
}
}
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 (GTK_WIDGET (obj->time));
gtk_widget_set_no_show_all (GTK_WIDGET (obj->calendar), !obj->show_date);
gtk_widget_set_no_show_all (GTK_WIDGET (obj->time), !obj->show_time);
// 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);
// 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->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), TRUE, TRUE, 0);
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"));
gtk_container_add (GTK_CONTAINER (obj->popup),
GTK_WIDGET (gtk_builder_get_object (builder, "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
));
}