1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
4
* Copyright (C) Álvaro Peña 2011-2012 <alvaropg@gmail.com>
6
* Almanah is free software: you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation, either version 3 of the License, or
9
* (at your option) any later version.
11
* Almanah is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with Almanah. If not, see <http://www.gnu.org/licenses/>.
20
#include <glib/gi18n.h>
23
#include "calendar-button.h"
25
#include "calendar-window.h"
26
#include "interface.h"
28
/* This enum allows to know the reason why the calendar date has been changed */
30
NONE_EVENT = 1, /* The window is showed */
31
FIRST_EVENT, /* The widget is instatiated the first time */
32
TODAY_EVENT, /* The user clicks on "Today" button */
33
DAY_EVENT, /* The user selects a concret day in the calendar widget */
34
MONTH_EVENT /* The user changes the month, or the year clicking in the calendar widget */
43
PROP_STORAGE_MANAGER = 1
46
static guint calendar_button_signals[LAST_SIGNAL] = { 0 };
48
struct _AlmanahCalendarButtonPrivate {
52
AlmanahCalendar *calendar;
53
GtkWidget *today_button;
54
GtkWidget *select_date_button;
55
AlmanahStorageManager *storage_manager;
58
static void almanah_calendar_button_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
59
static void almanah_calendar_button_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
60
static void almanah_calendar_button_finalize (GObject *object);
62
static void almanah_calendar_button_dock_hiden (GtkWidget *dock, AlmanahCalendarButton *self);
64
static void almanah_calendar_button_toggled (GtkToggleButton *togglebutton);
65
static void almanah_calendar_button_day_selected_cb (GtkCalendar *calendar, AlmanahCalendarButton *self);
66
static void almanah_calendar_button_month_changed_cb (GtkCalendar *calendar, AlmanahCalendarButton *self);
67
static gboolean almanah_calendar_button_today_press_cb (GtkWidget *widget, GdkEvent *event, AlmanahCalendarButton *self);
68
static gboolean almanah_calendar_button_select_date_press_cb (GtkWidget *widget, GdkEvent *event, AlmanahCalendarButton *self);
70
static void dock_position_func (AlmanahCalendarButton *self, gint *x, gint *y);
72
G_DEFINE_TYPE (AlmanahCalendarButton, almanah_calendar_button, GTK_TYPE_TOGGLE_BUTTON)
75
almanah_calendar_button_class_init (AlmanahCalendarButtonClass *klass)
77
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
78
GtkToggleButtonClass *toggle_button_class = GTK_TOGGLE_BUTTON_CLASS (klass);
80
g_type_class_add_private (klass, sizeof (AlmanahCalendarButtonPrivate));
82
gobject_class->get_property = almanah_calendar_button_get_property;
83
gobject_class->set_property = almanah_calendar_button_set_property;
84
gobject_class->finalize = almanah_calendar_button_finalize;
86
toggle_button_class->toggled = almanah_calendar_button_toggled;
88
g_object_class_install_property (gobject_class, PROP_STORAGE_MANAGER,
89
g_param_spec_object ("storage-manager",
90
"Storage manager", "The storage manager whose entries should be listed.",
91
ALMANAH_TYPE_STORAGE_MANAGER,
92
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
95
* AlmanahCalendarButton::day-selected:
96
* @calendar_button: the object which received the signal.
98
* Emitted when the user selects a day in the dock window.
100
calendar_button_signals[DAY_SELECTED_SIGNAL] = g_signal_new ("day-selected",
101
G_OBJECT_CLASS_TYPE (gobject_class),
103
G_STRUCT_OFFSET (AlmanahCalendarButtonClass, day_selected),
110
almanah_calendar_button_init (AlmanahCalendarButton *self)
115
GError *error = NULL;
116
const gchar *interface_filename = almanah_get_interface_filename ();
117
const gchar *object_names[] = {
118
"almanah_calendar_window",
122
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_CALENDAR_BUTTON, AlmanahCalendarButtonPrivate);
123
self->priv->user_event = FIRST_EVENT;
125
gtk_button_set_focus_on_click (GTK_BUTTON (self), TRUE);
127
/* The button elements */
128
self->priv->label = gtk_label_new (NULL);
130
arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
132
main_box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6));
133
gtk_box_pack_start (main_box, self->priv->label, TRUE, TRUE, 0);
134
gtk_box_pack_start (main_box, arrow, FALSE, TRUE, 0);
135
gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (main_box));
137
/* Calendar dock window from the UI file */
138
builder = gtk_builder_new ();
139
if (gtk_builder_add_objects_from_file (builder, interface_filename, (gchar **) object_names, &error) == FALSE) {
140
g_warning (_("UI file \"%s\" could not be loaded: %s"), interface_filename, error->message);
141
g_error_free (error);
142
g_object_unref (builder);
147
gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE);
148
self->priv->dock = GTK_WIDGET (gtk_builder_get_object (builder, "almanah_calendar_window"));
149
if (self->priv->dock == NULL) {
150
g_warning (_("Can't load calendar window object from UI file"));
151
g_object_unref (builder);
155
gtk_window_set_type_hint (GTK_WINDOW (self->priv->dock), GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU);
157
g_signal_connect (self->priv->dock, "hide", G_CALLBACK (almanah_calendar_button_dock_hiden), self);
159
/* The calendar widget */
160
self->priv->calendar = ALMANAH_CALENDAR (gtk_builder_get_object (builder, "almanah_cw_calendar"));
161
g_object_ref (self->priv->calendar);
162
g_signal_connect (self->priv->calendar, "day-selected", G_CALLBACK (almanah_calendar_button_day_selected_cb), self);
163
g_signal_connect (self->priv->calendar, "month_changed", G_CALLBACK (almanah_calendar_button_month_changed_cb), self);
166
self->priv->today_button = GTK_WIDGET (gtk_builder_get_object (builder, "almanah_cw_today_button"));
167
g_signal_connect (self->priv->today_button, "button-press-event", G_CALLBACK (almanah_calendar_button_today_press_cb), self);
169
/* Select a day button */
170
/* @TODO: No the button press event, instead the 'activate' action funcion (if not, the select day window dosn't showed... */
171
self->priv->select_date_button = GTK_WIDGET (gtk_builder_get_object (builder, "almanah_cw_select_date_button"));
172
g_signal_connect (self->priv->select_date_button, "button-press-event", G_CALLBACK (almanah_calendar_button_select_date_press_cb), self);
174
g_object_unref (builder);
178
almanah_calendar_button_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
180
AlmanahCalendarButtonPrivate *priv = ALMANAH_CALENDAR_BUTTON (object)->priv;
182
switch (property_id) {
183
case PROP_STORAGE_MANAGER:
184
g_value_set_object (value, priv->storage_manager);
187
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
193
almanah_calendar_button_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
195
AlmanahCalendarButton *self = ALMANAH_CALENDAR_BUTTON (object);
197
switch (property_id) {
198
case PROP_STORAGE_MANAGER:
199
almanah_calendar_button_set_storage_manager (self, g_value_get_object (value));
202
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
208
almanah_calendar_button_finalize (GObject *object)
210
AlmanahCalendarButtonPrivate *priv = ALMANAH_CALENDAR_BUTTON (object)->priv;
212
g_clear_object (&priv->calendar);
213
g_clear_object (&priv->storage_manager);
215
/* Chain up to the parent class */
216
G_OBJECT_CLASS (almanah_calendar_button_parent_class)->finalize (object);
220
* Calculate the window position
223
dock_position_func (AlmanahCalendarButton *self, gint *x, gint *y)
226
GdkRectangle monitor;
227
GtkAllocation allocation;
228
GtkRequisition dock_req;
229
gint new_x, new_y, monitor_num;
230
AlmanahCalendarWindow *calendar_window = ALMANAH_CALENDAR_WINDOW (self->priv->dock);
232
/* Get the screen and monitor geometry */
233
screen = gtk_widget_get_screen (GTK_WIDGET (self));
234
monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (GTK_WIDGET (self)));
237
gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
239
/* Get the AlmanahCalendarButton position */
240
gtk_widget_get_allocation (GTK_WIDGET (self), &allocation);
241
gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (self)), &new_x, &new_y);
242
/* The dock window starting position is over the calendar button widget */
243
new_x += allocation.x;
244
new_y += allocation.y;
246
gtk_widget_get_preferred_size (GTK_WIDGET (calendar_window), &dock_req, NULL);
247
if (new_x + dock_req.width > monitor.x + monitor.width) {
248
/* Move the required pixels to the left if the dock don't showed complety
251
new_x -= (new_x + dock_req.width) - (monitor.x + monitor.width);
254
if ((new_y + allocation.height + dock_req.height) <= monitor.y + monitor.height) {
255
/*The dock window height isn't bigger than the monitor size */
256
new_y += allocation.height;
257
} else if (new_y - dock_req.height >= monitor.y) {
258
/* If the dock window height can't showed complety in the monitor,
259
* and the dock height isn't to bigg to show on top the calendar button
260
* move it on top of the calendar button
262
new_y -= dock_req.height;
263
} else if (monitor.y + monitor.height - (new_y + allocation.height) > new_y) {
264
/* in other case, we show under the calendar button if the space is enought */
265
new_y += allocation.height;
267
/* we need to put the dock in somewhere... even the monitor is to small */
268
new_y -= dock_req.height;
271
/* Put the dock window in the correct screen */
272
gtk_window_set_screen (GTK_WINDOW (calendar_window), screen);
279
almanah_calendar_button_dock_hiden (GtkWidget *dock, AlmanahCalendarButton *self)
281
/* Reset the calendar user event and toggle off the button */
282
ALMANAH_CALENDAR_BUTTON (self)->priv->user_event = NONE_EVENT;
283
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self), FALSE);
287
almanah_calendar_button_toggled (GtkToggleButton *togglebutton)
290
AlmanahCalendarButton *self;
292
self = ALMANAH_CALENDAR_BUTTON (togglebutton);
293
if (gtk_toggle_button_get_active (togglebutton)) {
295
dock_position_func (self, &x, &y);
296
gtk_window_move (GTK_WINDOW (self->priv->dock), x, y);
297
almanah_calendar_window_popup (ALMANAH_CALENDAR_WINDOW (self->priv->dock));
302
almanah_calendar_button_day_selected_cb (GtkCalendar *calendar, AlmanahCalendarButton *self)
305
gchar calendar_string[100];
307
almanah_calendar_get_date (self->priv->calendar, &calendar_date);
308
/* Translators: This is a strftime()-format string for the date displayed at the top of the main window. */
309
g_date_strftime (calendar_string, sizeof (calendar_string), _("%A, %e %B %Y"), &calendar_date);
310
gtk_label_set_text (GTK_LABEL (self->priv->label), calendar_string);
311
if (self->priv->user_event < DAY_EVENT) {
312
/* Only hide the dock window when the user has clicked in a calendar day */
313
self->priv->user_event = DAY_EVENT;
314
almanah_calendar_window_popdown (ALMANAH_CALENDAR_WINDOW (self->priv->dock));
317
self->priv->user_event = NONE_EVENT;
319
/* Emmits the signal at the end */
320
g_signal_emit (self, calendar_button_signals[DAY_SELECTED_SIGNAL], 0);
324
almanah_calendar_button_month_changed_cb (GtkCalendar *calendar, AlmanahCalendarButton *self)
326
if (self->priv->user_event != TODAY_EVENT) {
327
/* Save the month changed event just if the user hasn't click the today button
328
* beacuse the dock window should not hide in this case */
329
self->priv->user_event = MONTH_EVENT;
334
almanah_calendar_button_today_press_cb (GtkWidget *widget, GdkEvent *event, AlmanahCalendarButton *self)
336
/* Save this event to not hide the dock window */
337
self->priv->user_event = TODAY_EVENT;
343
almanah_calendar_button_select_date_press_cb (GtkWidget *widget, GdkEvent *event, AlmanahCalendarButton *self)
345
self->priv->user_event = NONE_EVENT;
351
almanah_calendar_button_new (AlmanahStorageManager *storage_manager)
353
g_return_val_if_fail (ALMANAH_IS_STORAGE_MANAGER (storage_manager), NULL);
354
return GTK_WIDGET (g_object_new (ALMANAH_TYPE_CALENDAR_BUTTON, "storage-manager", storage_manager, NULL));
358
almanah_calendar_button_set_storage_manager (AlmanahCalendarButton *self, AlmanahStorageManager *storage_manager)
360
g_return_if_fail (ALMANAH_IS_CALENDAR_BUTTON (self));
361
g_return_if_fail (ALMANAH_IS_STORAGE_MANAGER (storage_manager));
363
g_clear_object (&self->priv->storage_manager);
364
self->priv->storage_manager = storage_manager;
365
g_object_ref (self->priv->storage_manager);
367
if (self->priv->calendar != NULL && ALMANAH_IS_CALENDAR (self->priv->calendar)) {
368
almanah_calendar_set_storage_manager (self->priv->calendar, self->priv->storage_manager);
373
almanah_calendar_button_set_today_action (AlmanahCalendarButton *self, GtkAction *action)
375
g_return_if_fail (ALMANAH_IS_CALENDAR_BUTTON (self));
376
g_return_if_fail (GTK_IS_ACTION (action));
378
if (GTK_IS_BUTTON (self->priv->today_button)) {
379
gtk_activatable_set_related_action (GTK_ACTIVATABLE (self->priv->today_button), action);
384
almanah_calendar_button_set_select_date_action (AlmanahCalendarButton *self, GtkAction *action)
386
g_return_if_fail (ALMANAH_IS_CALENDAR_BUTTON (self));
387
g_return_if_fail (GTK_IS_ACTION (action));
389
if (GTK_IS_BUTTON (self->priv->select_date_button)) {
390
gtk_activatable_set_related_action (GTK_ACTIVATABLE (self->priv->select_date_button), action);
395
almanah_calendar_button_select_date (AlmanahCalendarButton *self, GDate *date)
397
g_return_if_fail (ALMANAH_IS_CALENDAR_BUTTON (self));
398
g_return_if_fail (date != NULL);
400
almanah_calendar_select_date (self->priv->calendar, date);
404
almanah_calendar_button_get_date (AlmanahCalendarButton *self, GDate *date)
406
g_return_if_fail (ALMANAH_IS_CALENDAR_BUTTON (self));
407
g_return_if_fail (date != NULL);
409
almanah_calendar_get_date (self->priv->calendar, date);
413
almanah_calendar_button_popdown (AlmanahCalendarButton *self)
415
g_return_if_fail (ALMANAH_IS_CALENDAR_BUTTON (self));
417
almanah_calendar_window_popdown (ALMANAH_CALENDAR_WINDOW (self->priv->dock));