~ubuntu-branches/ubuntu/utopic/almanah/utopic-proposed

« back to all changes in this revision

Viewing changes to src/widgets/calendar-button.c

  • Committer: Package Import Robot
  • Author(s): Angel Abad
  • Date: 2013-09-20 17:22:17 UTC
  • mfrom: (1.3.5) (10.1.6 experimental)
  • Revision ID: package-import@ubuntu.com-20130920172217-m0ihvom627502smz
Tags: 0.10.8-2
Upload to unstable (Closes: #722029)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
 
2
/*
 
3
 * Almanah
 
4
 * Copyright (C) Álvaro Peña 2011-2012 <alvaropg@gmail.com>
 
5
 *
 
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.
 
10
 *
 
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.
 
15
 *
 
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/>.
 
18
 */
 
19
 
 
20
#include <glib/gi18n.h>
 
21
#include <config.h>
 
22
 
 
23
#include "calendar-button.h"
 
24
#include "calendar.h"
 
25
#include "calendar-window.h"
 
26
#include "interface.h"
 
27
 
 
28
/* This enum allows to know the reason why the calendar date has been changed */
 
29
enum {
 
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 */
 
35
};
 
36
 
 
37
enum {
 
38
        DAY_SELECTED_SIGNAL,
 
39
        LAST_SIGNAL
 
40
};
 
41
 
 
42
enum {
 
43
        PROP_STORAGE_MANAGER = 1
 
44
};
 
45
 
 
46
static guint calendar_button_signals[LAST_SIGNAL] = { 0 };
 
47
 
 
48
struct _AlmanahCalendarButtonPrivate {
 
49
        GtkWidget *label;
 
50
        GtkWidget *dock;
 
51
        guchar user_event;
 
52
        AlmanahCalendar *calendar;
 
53
        GtkWidget *today_button;
 
54
        GtkWidget *select_date_button;
 
55
        AlmanahStorageManager *storage_manager;
 
56
};
 
57
 
 
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);
 
61
 
 
62
static void almanah_calendar_button_dock_hiden (GtkWidget *dock, AlmanahCalendarButton *self);
 
63
 
 
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);
 
69
 
 
70
static void dock_position_func (AlmanahCalendarButton *self, gint *x, gint *y);
 
71
 
 
72
G_DEFINE_TYPE (AlmanahCalendarButton, almanah_calendar_button, GTK_TYPE_TOGGLE_BUTTON)
 
73
 
 
74
static void
 
75
almanah_calendar_button_class_init (AlmanahCalendarButtonClass *klass)
 
76
{
 
77
        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
78
        GtkToggleButtonClass *toggle_button_class = GTK_TOGGLE_BUTTON_CLASS (klass);
 
79
 
 
80
        g_type_class_add_private (klass, sizeof (AlmanahCalendarButtonPrivate));
 
81
 
 
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;
 
85
 
 
86
        toggle_button_class->toggled = almanah_calendar_button_toggled;
 
87
 
 
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));
 
93
 
 
94
        /**
 
95
         * AlmanahCalendarButton::day-selected:
 
96
         * @calendar_button: the object which received the signal.
 
97
         *
 
98
         * Emitted when the user selects a day in the dock window.
 
99
         */
 
100
        calendar_button_signals[DAY_SELECTED_SIGNAL] = g_signal_new ("day-selected",
 
101
                                                                     G_OBJECT_CLASS_TYPE (gobject_class),
 
102
                                                                     G_SIGNAL_RUN_FIRST,
 
103
                                                                     G_STRUCT_OFFSET (AlmanahCalendarButtonClass, day_selected),
 
104
                                                                     NULL, NULL,
 
105
                                                                     NULL,
 
106
                                                                     G_TYPE_NONE, 0);
 
107
}
 
108
 
 
109
static void
 
110
almanah_calendar_button_init (AlmanahCalendarButton *self)
 
111
{
 
112
        GtkWidget *arrow;
 
113
        GtkBox *main_box;
 
114
        GtkBuilder *builder;
 
115
        GError *error = NULL;
 
116
        const gchar *interface_filename = almanah_get_interface_filename ();
 
117
        const gchar *object_names[] = {
 
118
                "almanah_calendar_window",
 
119
                NULL
 
120
        };
 
121
 
 
122
        self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_CALENDAR_BUTTON, AlmanahCalendarButtonPrivate);
 
123
        self->priv->user_event = FIRST_EVENT;
 
124
 
 
125
        gtk_button_set_focus_on_click (GTK_BUTTON (self), TRUE);
 
126
 
 
127
        /* The button elements */
 
128
        self->priv->label = gtk_label_new (NULL);
 
129
 
 
130
        arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
 
131
 
 
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));
 
136
 
 
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);
 
143
 
 
144
                return;
 
145
        }
 
146
 
 
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);
 
152
 
 
153
                return;
 
154
        }
 
155
        gtk_window_set_type_hint (GTK_WINDOW (self->priv->dock), GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU);
 
156
 
 
157
        g_signal_connect (self->priv->dock, "hide", G_CALLBACK (almanah_calendar_button_dock_hiden), self);
 
158
 
 
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);
 
164
 
 
165
        /* Today button */
 
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);
 
168
 
 
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);
 
173
 
 
174
        g_object_unref (builder);
 
175
}
 
176
 
 
177
static void
 
178
almanah_calendar_button_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
 
179
{
 
180
        AlmanahCalendarButtonPrivate *priv = ALMANAH_CALENDAR_BUTTON (object)->priv;
 
181
 
 
182
        switch (property_id) {
 
183
                case PROP_STORAGE_MANAGER:
 
184
                        g_value_set_object (value, priv->storage_manager);
 
185
                        break;
 
186
                default:
 
187
                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
188
                        break;
 
189
        }
 
190
}
 
191
 
 
192
static void
 
193
almanah_calendar_button_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
 
194
{
 
195
        AlmanahCalendarButton *self = ALMANAH_CALENDAR_BUTTON (object);
 
196
 
 
197
        switch (property_id) {
 
198
                case PROP_STORAGE_MANAGER:
 
199
                        almanah_calendar_button_set_storage_manager (self, g_value_get_object (value));
 
200
                        break;
 
201
                default:
 
202
                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
203
                        break;
 
204
        }
 
205
}
 
206
 
 
207
static void
 
208
almanah_calendar_button_finalize (GObject *object)
 
209
{
 
210
        AlmanahCalendarButtonPrivate *priv = ALMANAH_CALENDAR_BUTTON (object)->priv;
 
211
 
 
212
        g_clear_object (&priv->calendar);
 
213
        g_clear_object (&priv->storage_manager);
 
214
 
 
215
        /* Chain up to the parent class */
 
216
        G_OBJECT_CLASS (almanah_calendar_button_parent_class)->finalize (object);
 
217
}
 
218
 
 
219
/**
 
220
 * Calculate the window position
 
221
 */
 
222
static void
 
223
dock_position_func (AlmanahCalendarButton *self, gint *x, gint *y)
 
224
{
 
225
        GdkScreen *screen;
 
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);
 
231
 
 
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)));
 
235
        if (monitor_num < 0)
 
236
                monitor_num = 0;
 
237
        gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
 
238
 
 
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;
 
245
 
 
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
 
249
                 * in the screen
 
250
                 */
 
251
                new_x -= (new_x + dock_req.width) - (monitor.x + monitor.width);
 
252
        }
 
253
 
 
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
 
261
                 */
 
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;
 
266
        } else {
 
267
                /* we need to put the dock in somewhere... even the monitor is to small */
 
268
                new_y -= dock_req.height;
 
269
        }
 
270
 
 
271
        /* Put the dock window in the correct screen */
 
272
        gtk_window_set_screen (GTK_WINDOW (calendar_window), screen);
 
273
 
 
274
        *x = new_x;
 
275
        *y = new_y;
 
276
}
 
277
 
 
278
static void
 
279
almanah_calendar_button_dock_hiden (GtkWidget *dock, AlmanahCalendarButton *self)
 
280
{
 
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);
 
284
}
 
285
 
 
286
static void
 
287
almanah_calendar_button_toggled (GtkToggleButton *togglebutton)
 
288
{
 
289
        gint x, y;
 
290
        AlmanahCalendarButton *self;
 
291
 
 
292
        self = ALMANAH_CALENDAR_BUTTON (togglebutton);
 
293
        if (gtk_toggle_button_get_active (togglebutton)) {
 
294
                /* Show the dock */
 
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));
 
298
        }
 
299
}
 
300
 
 
301
static void
 
302
almanah_calendar_button_day_selected_cb (GtkCalendar *calendar, AlmanahCalendarButton *self)
 
303
{
 
304
        GDate calendar_date;
 
305
        gchar calendar_string[100];
 
306
 
 
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));
 
315
        }
 
316
 
 
317
        self->priv->user_event = NONE_EVENT;
 
318
 
 
319
        /* Emmits the signal at the end */
 
320
        g_signal_emit (self, calendar_button_signals[DAY_SELECTED_SIGNAL], 0);
 
321
}
 
322
 
 
323
static void
 
324
almanah_calendar_button_month_changed_cb (GtkCalendar *calendar, AlmanahCalendarButton *self)
 
325
{
 
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;
 
330
        }
 
331
}
 
332
 
 
333
static gboolean
 
334
almanah_calendar_button_today_press_cb (GtkWidget *widget, GdkEvent *event, AlmanahCalendarButton *self)
 
335
{
 
336
        /* Save this event to not hide the dock window */
 
337
        self->priv->user_event = TODAY_EVENT;
 
338
 
 
339
        return FALSE;
 
340
}
 
341
 
 
342
static gboolean
 
343
almanah_calendar_button_select_date_press_cb (GtkWidget *widget, GdkEvent *event, AlmanahCalendarButton *self)
 
344
{
 
345
        self->priv->user_event = NONE_EVENT;
 
346
 
 
347
        return FALSE;
 
348
}
 
349
 
 
350
GtkWidget *
 
351
almanah_calendar_button_new (AlmanahStorageManager *storage_manager)
 
352
{
 
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));
 
355
}
 
356
 
 
357
void
 
358
almanah_calendar_button_set_storage_manager (AlmanahCalendarButton *self, AlmanahStorageManager *storage_manager)
 
359
{
 
360
        g_return_if_fail (ALMANAH_IS_CALENDAR_BUTTON (self));
 
361
        g_return_if_fail (ALMANAH_IS_STORAGE_MANAGER (storage_manager));
 
362
 
 
363
        g_clear_object (&self->priv->storage_manager);
 
364
        self->priv->storage_manager = storage_manager;
 
365
        g_object_ref (self->priv->storage_manager);
 
366
 
 
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);
 
369
        }
 
370
}
 
371
 
 
372
void
 
373
almanah_calendar_button_set_today_action (AlmanahCalendarButton *self, GtkAction *action)
 
374
{
 
375
        g_return_if_fail (ALMANAH_IS_CALENDAR_BUTTON (self));
 
376
        g_return_if_fail (GTK_IS_ACTION (action));
 
377
 
 
378
        if (GTK_IS_BUTTON (self->priv->today_button)) {
 
379
                gtk_activatable_set_related_action (GTK_ACTIVATABLE (self->priv->today_button), action);
 
380
        }
 
381
}
 
382
 
 
383
void
 
384
almanah_calendar_button_set_select_date_action (AlmanahCalendarButton *self, GtkAction *action)
 
385
{
 
386
        g_return_if_fail (ALMANAH_IS_CALENDAR_BUTTON (self));
 
387
        g_return_if_fail (GTK_IS_ACTION (action));
 
388
 
 
389
        if (GTK_IS_BUTTON (self->priv->select_date_button)) {
 
390
                gtk_activatable_set_related_action (GTK_ACTIVATABLE (self->priv->select_date_button), action);
 
391
        }
 
392
}
 
393
 
 
394
void
 
395
almanah_calendar_button_select_date (AlmanahCalendarButton *self, GDate *date)
 
396
{
 
397
        g_return_if_fail (ALMANAH_IS_CALENDAR_BUTTON (self));
 
398
        g_return_if_fail (date != NULL);
 
399
 
 
400
        almanah_calendar_select_date (self->priv->calendar, date);
 
401
}
 
402
 
 
403
void
 
404
almanah_calendar_button_get_date (AlmanahCalendarButton *self, GDate *date)
 
405
{
 
406
        g_return_if_fail (ALMANAH_IS_CALENDAR_BUTTON (self));
 
407
        g_return_if_fail (date != NULL);
 
408
 
 
409
        almanah_calendar_get_date (self->priv->calendar, date);
 
410
}
 
411
 
 
412
void
 
413
almanah_calendar_button_popdown (AlmanahCalendarButton *self)
 
414
{
 
415
        g_return_if_fail (ALMANAH_IS_CALENDAR_BUTTON (self));
 
416
 
 
417
        almanah_calendar_window_popdown (ALMANAH_CALENDAR_WINDOW (self->priv->dock));
 
418
}