edit-dialog: add 'Repeat' options for simple recurrences

Currently, creating recurring events is not supported
in gnome-calendar.

This patch adds a Repeat combobox with a list of
simple recurrences such as:

 * Monday - Friday events
 * Daily events
 * Weekly events
 * Monthly events
 * Yearly events

and also a list of recurrence-duration-types such as:

 * If an event should repeat forever
 * A date until which the event should repeat
 * Number of times an event should repeat

It enables the user to create simple recurring events and
even modify the recurrence-property of an already
recurring event.

https://bugzilla.gnome.org/show_bug.cgi?id=782755
This commit is contained in:
Yash Singh
2017-07-13 13:32:53 -03:00
committed by Georges Basile Stavracas Neto
parent 2c75e72582
commit 067e8ae765
3 changed files with 291 additions and 28 deletions
+103 -6
View File
@@ -164,6 +164,22 @@
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="xalign">1.0</property>
<property name="label" translatable="yes">Repeat</property>
<property name="ellipsize">end</property>
<property name="sensitive" bind-source="GcalEditDialog" bind-property="writable" bind-flags="default" />
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="reminders_label">
<property name="visible">True</property>
@@ -178,7 +194,7 @@
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
<property name="top_attach">6</property>
</packing>
</child>
<child>
@@ -194,7 +210,7 @@
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
<property name="top_attach">8</property>
</packing>
</child>
<child>
@@ -209,7 +225,7 @@
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
<property name="top_attach">7</property>
</packing>
</child>
<child>
@@ -395,12 +411,93 @@
</object>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">6</property>
<property name="width">4</property>
</packing>
</child>
<!-- combo-box with options to define the recurrence-rules !-->
<child>
<object class="GtkComboBoxText" id="repeat_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<signal name="changed" handler="repeat_type_changed" object="GcalEditDialog" swapped="no"/>
<items>
<item translatable="yes" id="no_repeat">No Repeat</item>
<item translatable="yes" id="daily">Daily</item>
<item translatable="yes" id="mon_fri">Monday - Friday</item>
<item translatable="yes" id="weekly">Weekly</item>
<item translatable="yes" id="monthly">Monthly</item>
<item translatable="yes" id="yearly">Yearly</item>
</items>
<property name="active_id">no_repeat</property>
<style>
<class name="background" />
</style>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
<property name="width">4</property>
</packing>
</child>
<child>
<object class="GtkBox" id="repeat_limits_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<property name="hexpand">True</property>
<property name="orientation">horizontal</property>
<property name="sensitive" bind-source="GcalEditDialog" bind-property="writable" bind-flags="default" />
<child>
<object class="GtkComboBoxText" id="repeat_duration_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<signal name="changed" handler="repeat_duration_changed" object="GcalEditDialog" swapped="no"/>
<items>
<item translatable="yes" id="forever">Forever</item>
<item translatable="yes" id="number_of_occurrences_spin">No. of occurrences</item>
<item translatable="yes" id="until_date">Until Date</item>
</items>
<property name="active_id">forever</property>
<style>
<class name="background" />
</style>
</object>
</child>
<child>
<object class="GtkStack" id="repeat_duration_stack">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<child>
<object class = "GtkSpinButton" id="number_of_occurrences_spin">
<property name="visible">True</property>
<property name="input_purpose">number</property>
</object>
</child>
<child>
<object class = "GcalDateSelector" id="until_date_selector">
<property name="visible">True</property>
</object>
</child>
<style>
<class name="background" />
</style>
</object>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
<property name="width">4</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="location_entry">
<property name="visible">True</property>
@@ -412,7 +509,7 @@
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
<property name="top_attach">7</property>
<property name="width">4</property>
</packing>
</child>
@@ -441,7 +538,7 @@
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">6</property>
<property name="top_attach">8</property>
<property name="width">4</property>
</packing>
</child>
@@ -463,7 +560,7 @@
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">7</property>
<property name="top_attach">9</property>
</packing>
</child>
</object>
+186 -22
View File
@@ -25,9 +25,12 @@
#include "gcal-edit-dialog.h"
#include "gcal-time-selector.h"
#include "gcal-utils.h"
#include "gcal-event.h"
#include "gcal-recurrence.h"
#include <libecal/libecal.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
/**
* SECTION:gcal-edit-dialog
@@ -75,6 +78,15 @@ struct _GcalEditDialog
GtkWidget *alarms_listbox;
GtkWidget *repeat_combo;
GtkWidget *repeat_duration_combo;
GtkWidget *repeat_limits_box;
/* Recurrence widgets */
GtkWidget *number_of_occurrences_spin;
GtkWidget *repeat_duration_stack;
GtkWidget *until_date_selector;
/* Add Alarms popover buttons */
GtkWidget *five_minutes_button;
GtkWidget *ten_minutes_button;
@@ -96,40 +108,41 @@ struct _GcalEditDialog
/* flags */
gboolean format_24h;
gboolean event_is_new;
gboolean recurrence_changed;
gboolean setting_event;
};
static void fill_sources_menu (GcalEditDialog *dialog);
static void fill_sources_menu (GcalEditDialog *dialog);
static void on_calendar_selected (GSimpleAction *menu_item,
GVariant *value,
gpointer user_data);
static void on_calendar_selected (GSimpleAction *menu_item,
GVariant *value,
gpointer user_data);
static void update_location (GtkEntry *entry,
GParamSpec *pspec,
gpointer user_data);
static void update_location (GtkEntry *entry,
GParamSpec *pspec,
gpointer user_data);
static void update_summary (GtkEntry *entry,
GParamSpec *pspec,
gpointer user_data);
static void update_summary (GtkEntry *entry,
GParamSpec *pspec,
gpointer user_data);
static void gcal_edit_dialog_constructed (GObject *object);
static void gcal_edit_dialog_constructed (GObject *object);
static void gcal_edit_dialog_finalize (GObject *object);
static void gcal_edit_dialog_finalize (GObject *object);
static void gcal_edit_dialog_set_writable (GcalEditDialog *dialog,
gboolean writable);
static void gcal_edit_dialog_set_writable (GcalEditDialog *dialog,
gboolean writable);
static void gcal_edit_dialog_clear_data (GcalEditDialog *dialog);
static void gcal_edit_dialog_clear_data (GcalEditDialog *dialog);
static void gcal_edit_dialog_action_button_clicked (GtkWidget *widget,
gpointer user_data);
static void gcal_edit_dialog_action_button_clicked (GtkWidget *widget,
gpointer user_data);
static void gcal_edit_dialog_all_day_changed (GtkWidget *widget,
gpointer user_data);
static void gcal_edit_dialog_all_day_changed (GtkWidget *widget,
gpointer user_data);
static void add_alarm_button_clicked (GtkWidget *button,
GcalEditDialog *self);
static void add_alarm_button_clicked (GtkWidget *button,
GcalEditDialog *self);
G_DEFINE_TYPE (GcalEditDialog, gcal_edit_dialog, GTK_TYPE_DIALOG)
@@ -268,6 +281,29 @@ on_calendar_selected (GSimpleAction *action,
GCAL_EXIT;
}
static void
remove_recurrence_properties (GcalEvent *event)
{
ECalComponent *comp;
icalcomponent *icalcomp;
icalproperty *prop;
comp = gcal_event_get_component (event);
icalcomp = e_cal_component_get_icalcomponent (comp);
e_cal_component_set_recurid (comp, NULL);
prop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY);
if (prop)
{
icalcomponent_remove_property (icalcomp, prop);
icalproperty_free (prop);
}
e_cal_component_rescan (comp);
}
static void
update_location (GtkEntry *entry,
GParamSpec *pspec,
@@ -442,6 +478,50 @@ gcal_edit_dialog_set_property (GObject *object,
}
}
static void
repeat_duration_changed (GtkComboBox *widget,
GcalEditDialog *self)
{
switch (gtk_combo_box_get_active (widget))
{
case 0:
gtk_widget_hide (self->repeat_duration_stack);
break;
case 1:
gtk_widget_show (self->repeat_duration_stack);
gtk_stack_set_visible_child (GTK_STACK (self->repeat_duration_stack), self->number_of_occurrences_spin);
break;
case 2:
gtk_widget_show (self->repeat_duration_stack);
gtk_stack_set_visible_child (GTK_STACK (self->repeat_duration_stack), self->until_date_selector);
break;
default:
break;
}
}
static void
repeat_type_changed (GtkComboBox *combobox,
GcalEditDialog *self)
{
GcalRecurrenceFrequency frequency;
gboolean has_recurrence;
frequency = gtk_combo_box_get_active (combobox);
has_recurrence = frequency != GCAL_RECURRENCE_NO_REPEAT;
gtk_widget_set_visible (self->repeat_limits_box, has_recurrence);
if (has_recurrence)
{
gtk_combo_box_set_active (GTK_COMBO_BOX (self->repeat_duration_combo), GCAL_RECURRENCE_FOREVER);
gtk_widget_hide (self->repeat_duration_stack);
}
}
static void
gcal_edit_dialog_class_init (GcalEditDialogClass *klass)
{
@@ -525,18 +605,27 @@ gcal_edit_dialog_class_init (GcalEditDialogClass *klass)
gtk_widget_class_bind_template_child (widget_class, GcalEditDialog, title_label);
gtk_widget_class_bind_template_child (widget_class, GcalEditDialog, subtitle_label);
gtk_widget_class_bind_template_child (widget_class, GcalEditDialog, lock);
gtk_widget_class_bind_template_child (widget_class, GcalEditDialog, number_of_occurrences_spin);
gtk_widget_class_bind_template_child (widget_class, GcalEditDialog, repeat_combo);
gtk_widget_class_bind_template_child (widget_class, GcalEditDialog, repeat_duration_combo);
gtk_widget_class_bind_template_child (widget_class, GcalEditDialog, repeat_duration_stack);
gtk_widget_class_bind_template_child (widget_class, GcalEditDialog, repeat_limits_box);
gtk_widget_class_bind_template_child (widget_class, GcalEditDialog, source_image);
gtk_widget_class_bind_template_child (widget_class, GcalEditDialog, sources_popover);
gtk_widget_class_bind_template_child (widget_class, GcalEditDialog, until_date_selector);
/* callbacks */
gtk_widget_class_bind_template_callback (widget_class, add_alarm_button_clicked);
gtk_widget_class_bind_template_callback (widget_class, fix_reminders_label_height_cb);
gtk_widget_class_bind_template_callback (widget_class, gcal_edit_dialog_action_button_clicked);
gtk_widget_class_bind_template_callback (widget_class, gcal_edit_dialog_all_day_changed);
gtk_widget_class_bind_template_callback (widget_class, repeat_duration_changed);
gtk_widget_class_bind_template_callback (widget_class, repeat_type_changed);
gtk_widget_class_bind_template_callback (widget_class, sync_datetimes);
gtk_widget_class_bind_template_callback (widget_class, update_summary);
gtk_widget_class_bind_template_callback (widget_class, update_location);
gtk_widget_class_bind_template_callback (widget_class, update_revealer_visibility_cb);
gtk_widget_class_bind_template_callback (widget_class, sync_datetimes);
}
static void
@@ -659,6 +748,8 @@ gcal_edit_dialog_action_button_clicked (GtkWidget *widget,
}
else
{
GcalRecurrenceFrequency freq;
GcalRecurrence *old_recur;
GDateTime *start_date, *end_date;
gboolean all_day;
gchar *note_text;
@@ -715,6 +806,34 @@ gcal_edit_dialog_action_button_clicked (GtkWidget *widget,
g_clear_pointer (&start_date, g_date_time_unref);
g_clear_pointer (&end_date, g_date_time_unref);
/* Check Repeat popover and set recurrence-rules accordingly */
old_recur = gcal_event_get_recurrence (dialog->event);
freq = gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->repeat_combo));
remove_recurrence_properties (dialog->event);
if ((!old_recur && freq != GCAL_RECURRENCE_NO_REPEAT) || old_recur->frequency != freq)
dialog->recurrence_changed = TRUE;
if (freq != GCAL_RECURRENCE_NO_REPEAT)
{
GcalRecurrence *recur;
recur = gcal_recurrence_new ();
recur->frequency = freq;
recur->limit_type = gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->repeat_duration_combo));
if (recur->limit_type == GCAL_RECURRENCE_UNTIL)
recur->limit.until = gcal_date_selector_get_date (GCAL_DATE_SELECTOR (dialog->until_date_selector));
else if (recur->limit_type == GCAL_RECURRENCE_COUNT)
recur->limit.count = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (dialog->number_of_occurrences_spin));
if (!gcal_recurrence_is_equal (old_recur, recur))
gcal_event_set_recurrence (dialog->event, recur);
g_clear_pointer (&recur, gcal_recurrence_free);
}
/* Update the source if needed */
if (dialog->selected_source &&
gcal_event_get_source (dialog->event) != dialog->selected_source)
@@ -1123,6 +1242,10 @@ void
gcal_edit_dialog_set_event (GcalEditDialog *dialog,
GcalEvent *event)
{
GcalRecurrenceLimitType limit_type;
GcalRecurrenceFrequency frequency;
GcalRecurrence *recur;
GtkAdjustment *count_adjustment;
GDateTime *date_start;
GDateTime *date_end;
cairo_surface_t *surface;
@@ -1138,10 +1261,37 @@ gcal_edit_dialog_set_event (GcalEditDialog *dialog,
dialog->setting_event = TRUE;
count_adjustment = gtk_adjustment_new (0, 0, 1000, 1, 1, 10);
gtk_spin_button_set_adjustment (GTK_SPIN_BUTTON (dialog->number_of_occurrences_spin), count_adjustment);
/* If we just set the event to NULL, simply send a property notify */
if (!event)
GCAL_GOTO (out);
/* Recurrences */
recur = gcal_event_get_recurrence (event);
frequency = recur ? recur->frequency : GCAL_RECURRENCE_NO_REPEAT;
limit_type = recur ? recur->limit_type : GCAL_RECURRENCE_FOREVER;
gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->repeat_duration_combo), limit_type);
gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->repeat_combo), frequency);
switch (limit_type)
{
case GCAL_RECURRENCE_COUNT:
gtk_spin_button_set_value (GTK_SPIN_BUTTON (dialog->number_of_occurrences_spin), recur->limit.count);
break;
case GCAL_RECURRENCE_UNTIL:
gcal_date_selector_set_date (GCAL_DATE_SELECTOR (dialog->until_date_selector), recur->limit.until);
break;
case GCAL_RECURRENCE_FOREVER:
gtk_spin_button_set_value (GTK_SPIN_BUTTON (dialog->number_of_occurrences_spin), 0);
break;
}
all_day = gcal_event_get_all_day (event);
source = gcal_event_get_source (event);
@@ -1204,6 +1354,11 @@ gcal_edit_dialog_set_event (GcalEditDialog *dialog,
/* all_day */
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->all_day_check), all_day);
/* recurrence_changed */
dialog->recurrence_changed = FALSE;
gtk_adjustment_set_value (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (dialog->number_of_occurrences_spin)), 0);
/* location */
gtk_entry_set_text (GTK_ENTRY (dialog->location_entry), gcal_event_get_location (event));
@@ -1327,3 +1482,12 @@ gcal_edit_dialog_get_date_end (GcalEditDialog *dialog)
GCAL_DATE_SELECTOR (dialog->end_date_selector),
GCAL_TIME_SELECTOR (dialog->end_time_selector));
}
gboolean
gcal_edit_dialog_get_recurrence_changed (GcalEditDialog *self)
{
g_return_val_if_fail (GCAL_IS_EDIT_DIALOG (self), FALSE);
return self->recurrence_changed;
}
+2
View File
@@ -55,6 +55,8 @@ GDateTime* gcal_edit_dialog_get_date_end (GcalEditDialog *d
GDateTime* gcal_edit_dialog_get_date_start (GcalEditDialog *dialog);
gboolean gcal_edit_dialog_get_recurrence_changed (GcalEditDialog *self);
G_END_DECLS
#endif /* __GCAL_EDIT_DIALOG_H__ */