[gnome-calendar] edit-dialog: add 'Repeat' options for simple recurrences



commit 067e8ae765927211a9c49514f947523cde6cf721
Author: Yash Singh <yashdev10p gmail com>
Date:   Wed Jul 12 02:04:15 2017 +0530

    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

 data/ui/edit-dialog.ui |  109 ++++++++++++++++++++++++--
 src/gcal-edit-dialog.c |  208 +++++++++++++++++++++++++++++++++++++++++++-----
 src/gcal-edit-dialog.h |    2 +
 3 files changed, 291 insertions(+), 28 deletions(-)
---
diff --git a/data/ui/edit-dialog.ui b/data/ui/edit-dialog.ui
index 5bdf828..e51ef19 100644
--- a/data/ui/edit-dialog.ui
+++ b/data/ui/edit-dialog.ui
@@ -165,6 +165,22 @@
               </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>
                 <property name="xalign">1.0</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>
@@ -397,11 +413,92 @@
               </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>
                 <property name="can_focus">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>
diff --git a/src/gcal-edit-dialog.c b/src/gcal-edit-dialog.c
index 85d0435..156e970 100644
--- a/src/gcal-edit-dialog.c
+++ b/src/gcal-edit-dialog.c
@@ -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)
 
@@ -269,6 +282,29 @@ on_calendar_selected (GSimpleAction *action,
 }
 
 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,
                  gpointer    user_data)
@@ -443,6 +479,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)
 {
   GObjectClass *object_class;
@@ -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;
+}
diff --git a/src/gcal-edit-dialog.h b/src/gcal-edit-dialog.h
index 1dff9a1..17b5253 100644
--- a/src/gcal-edit-dialog.h
+++ b/src/gcal-edit-dialog.h
@@ -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__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]