[evolution] I#399 - Allow Selection From Multiple User-Defined Reminder Times



commit fbe4b92b595fa5bfbc2827c86793ac37313e11fc
Author: Milan Crha <mcrha redhat com>
Date:   Tue Jun 11 18:18:27 2019 +0200

    I#399 - Allow Selection From Multiple User-Defined Reminder Times
    
    Closes https://gitlab.gnome.org/GNOME/evolution/issues/399

 data/org.gnome.evolution.calendar.gschema.xml.in |   4 +
 src/calendar/gui/e-alarm-list.c                  |  45 +-
 src/calendar/gui/e-comp-editor-page-reminders.c  | 700 +++++++++++++++++------
 3 files changed, 523 insertions(+), 226 deletions(-)
---
diff --git a/data/org.gnome.evolution.calendar.gschema.xml.in 
b/data/org.gnome.evolution.calendar.gschema.xml.in
index 4813b6e14a..3fe4a630ec 100644
--- a/data/org.gnome.evolution.calendar.gschema.xml.in
+++ b/data/org.gnome.evolution.calendar.gschema.xml.in
@@ -457,6 +457,10 @@
       <default>false</default>
       <_summary>Allow direct edit of event Summary when clicking on it in the Day, Work Week, Week or Month 
view.</_summary>
     </key>
+    <key name="custom-reminders-minutes" type="ai">
+      <default>[]</default>
+      <_summary>User-defined reminder times, in minutes</_summary>
+    </key>
 
     <!-- The following keys are deprecated. -->
 
diff --git a/src/calendar/gui/e-alarm-list.c b/src/calendar/gui/e-alarm-list.c
index 3b0d47c988..2509d9782c 100644
--- a/src/calendar/gui/e-alarm-list.c
+++ b/src/calendar/gui/e-alarm-list.c
@@ -357,48 +357,17 @@ e_alarm_list_get_path (GtkTreeModel *tree_model,
 static gchar *
 get_alarm_duration_string (ICalDuration *duration)
 {
-       GString *string = g_string_new (NULL);
-       gboolean have_something;
-       guint value;
+       gint seconds = 0;
 
-       have_something = FALSE;
+       seconds = i_cal_duration_as_int (duration);
 
-       value = i_cal_duration_get_days (duration);
-       if (value >= 1) {
-               /* Translator: Entire string is like "Pop up an alert %d days before start" */
-               g_string_printf (string, ngettext ("%d day", "%d days", value), value);
-               have_something = TRUE;
-       }
-
-       value = i_cal_duration_get_weeks (duration);
-       if (value >= 1) {
-               /* Translator: Entire string is like "Pop up an alert %d weeks before start" */
-               g_string_printf (string, ngettext ("%d week","%d weeks", value), value);
-               have_something = TRUE;
-       }
-
-       value = i_cal_duration_get_hours (duration);
-       if (value >= 1) {
-               /* Translator: Entire string is like "Pop up an alert %d hours before start" */
-               g_string_printf (string, ngettext ("%d hour", "%d hours", value), value);
-               have_something = TRUE;
-       }
-
-       value = i_cal_duration_get_minutes (duration);
-       if (value >= 1) {
-               /* Translator: Entire string is like "Pop up an alert %d minutes before start" */
-               g_string_printf (string, ngettext ("%d minute", "%d minutes", value), value);
-               have_something = TRUE;
-       }
+       if (!seconds)
+               return NULL;
 
-       value = i_cal_duration_get_seconds (duration);
-       if (value >= 1) {
-               /* Translator: Entire string is like "Pop up an alert %d seconds before start" */
-               g_string_printf (string, ngettext ("%d second", "%d seconds", value), value);
-               have_something = TRUE;
-       }
+       if (seconds < 0)
+               seconds *= -1;
 
-       return g_string_free (string, !have_something);
+       return e_cal_util_seconds_to_string (seconds);
 }
 
 static gchar *
diff --git a/src/calendar/gui/e-comp-editor-page-reminders.c b/src/calendar/gui/e-comp-editor-page-reminders.c
index 1708886cc4..f2f1076b63 100644
--- a/src/calendar/gui/e-comp-editor-page-reminders.c
+++ b/src/calendar/gui/e-comp-editor-page-reminders.c
@@ -39,33 +39,10 @@
 #define SECTION_NAME                   _("Send To")
 #define X_EVOLUTION_NEEDS_DESCRIPTION  "X-EVOLUTION-NEEDS-DESCRIPTION"
 
-enum {
-       ALARM_NONE,
-       ALARM_15_MINUTES,
-       ALARM_1_HOUR,
-       ALARM_1_DAY,
-       ALARM_USER_TIME,
-       ALARM_CUSTOM
-};
-
-static const gint alarm_map_with_user_time[] = {
-       ALARM_NONE,
-       ALARM_15_MINUTES,
-       ALARM_1_HOUR,
-       ALARM_1_DAY,
-       ALARM_USER_TIME,
-       ALARM_CUSTOM,
-       -1
-};
-
-static const gint alarm_map_without_user_time[] = {
-       ALARM_NONE,
-       ALARM_15_MINUTES,
-       ALARM_1_HOUR,
-       ALARM_1_DAY,
-       ALARM_CUSTOM,
-       -1
-};
+#define N_PREDEFINED_ALARMS            3
+#define N_MAX_PREDEFINED_USER_ALARMS   10
+/* The 3 = 1 for the default alarm + 1 for None + 1 for Custom */
+#define N_MAX_PREDEFINED_ALARMS                ((N_PREDEFINED_ALARMS) + (N_MAX_PREDEFINED_USER_ALARMS) + 3)
 
 /* "relative" types */
 enum {
@@ -130,6 +107,7 @@ static const gint duration_units_map[] = {
 
 struct _ECompEditorPageRemindersPrivate {
        GtkWidget *alarms_combo;
+       GtkWidget *remove_custom_times_button;
        GtkWidget *alarms_scrolled_window;
        GtkWidget *alarms_tree_view;
        GtkWidget *alarms_button_box;
@@ -161,11 +139,15 @@ struct _ECompEditorPageRemindersPrivate {
        GtkWidget *custom_email_message_check;
        GtkWidget *custom_email_message_text_view;
 
+       GtkWidget *add_custom_time_popover;
+       GtkWidget *add_custom_time_days_spin;
+       GtkWidget *add_custom_time_hours_spin;
+       GtkWidget *add_custom_time_minutes_spin;
+       GtkWidget *add_custom_time_add_button;
+
        EAlarmList *alarm_list;
-       EDurationType alarm_units;
-       gint alarm_interval;
-       /* either with-user-time or without it */
-       const gint *alarm_map;
+       /* Interval in minutes. */
+       gint predefined_alarms[N_MAX_PREDEFINED_ALARMS];
 
        /* Addressbook name selector, created on demand */
        ENameSelector *name_selector;
@@ -173,6 +155,29 @@ struct _ECompEditorPageRemindersPrivate {
 
 G_DEFINE_TYPE (ECompEditorPageReminders, e_comp_editor_page_reminders, E_TYPE_COMP_EDITOR_PAGE)
 
+static gint
+ecep_reminders_get_alarm_index (GtkComboBox *combo_box)
+{
+       GtkTreeModel *model;
+       gint alarm_index;
+
+       g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
+
+       alarm_index = gtk_combo_box_get_active (combo_box);
+       if (alarm_index == -1)
+               return alarm_index;
+
+       model = gtk_combo_box_get_model (combo_box);
+       if (!model)
+               return -1;
+
+       /* The Custom alarm is always the last item */
+       if (alarm_index == gtk_tree_model_iter_n_children (model, NULL) - 1)
+               alarm_index = -2;
+
+       return alarm_index;
+}
+
 static void
 ecep_reminders_sanitize_option_widgets (ECompEditorPageReminders *page_reminders)
 {
@@ -184,8 +189,7 @@ ecep_reminders_sanitize_option_widgets (ECompEditorPageReminders *page_reminders
 
        any_selected = gtk_tree_selection_count_selected_rows (gtk_tree_view_get_selection (
                GTK_TREE_VIEW (page_reminders->priv->alarms_tree_view))) > 0;
-       is_custom = e_dialog_combo_box_get (page_reminders->priv->alarms_combo,
-               page_reminders->priv->alarm_map) == ALARM_CUSTOM;
+       is_custom = ecep_reminders_get_alarm_index (GTK_COMBO_BOX (page_reminders->priv->alarms_combo)) == -2;
 
        gtk_widget_set_sensitive (page_reminders->priv->alarms_tree_view, is_custom);
        gtk_widget_set_sensitive (page_reminders->priv->alarms_add_button, is_custom);
@@ -360,6 +364,7 @@ ecep_reminders_selected_to_widgets (ECompEditorPageReminders *page_reminders)
        ICalDuration *duration;
        GtkTreeSelection *selection;
        GtkTreeIter iter;
+       gint duration_minutes = 0;
 
        g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
 
@@ -400,18 +405,22 @@ ecep_reminders_selected_to_widgets (ECompEditorPageReminders *page_reminders)
        else
                e_dialog_combo_box_set (page_reminders->priv->relative_time_combo, AFTER, relative_map);
 
-       if (duration && i_cal_duration_get_days (duration)) {
+       if (duration) {
+               duration_minutes = i_cal_duration_as_int (duration) / 60;
+
+               if (duration_minutes < 0)
+                       duration_minutes *= -1;
+       }
+
+       if (duration_minutes && !(duration_minutes % (24 * 60))) {
                e_dialog_combo_box_set (page_reminders->priv->unit_combo, DAYS, value_map);
-               gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->time_spin),
-                       i_cal_duration_get_days (duration));
-       } else if (duration && i_cal_duration_get_hours (duration)) {
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->time_spin), 
duration_minutes / (24 * 60));
+       } else if (duration_minutes && !(duration_minutes % 60)) {
                e_dialog_combo_box_set (page_reminders->priv->unit_combo, HOURS, value_map);
-               gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->time_spin),
-                       i_cal_duration_get_hours (duration));
-       } else if (duration && i_cal_duration_get_minutes (duration)) {
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->time_spin), 
duration_minutes / 60);
+       } else if (duration_minutes) {
                e_dialog_combo_box_set (page_reminders->priv->unit_combo, MINUTES, value_map);
-               gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->time_spin),
-                       i_cal_duration_get_minutes (duration));
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->time_spin), 
duration_minutes);
        } else {
                e_dialog_combo_box_set (page_reminders->priv->unit_combo, MINUTES, value_map);
                gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->time_spin), 0);
@@ -803,13 +812,32 @@ ecep_reminders_alarms_selection_changed_cb (GtkTreeSelection *selection,
                ecep_reminders_selected_to_widgets (page_reminders);
 }
 
+static gint
+ecep_reminders_interval_to_int (gint days,
+                               gint hours,
+                               gint minutes)
+{
+       return (days * 24 * 60) + (hours * 60) + minutes;
+}
+
+static void
+ecep_reminders_int_to_interval (gint value,
+                               gint *out_days,
+                               gint *out_hours,
+                               gint *out_minutes)
+{
+       *out_days = value / (24 * 60);
+       *out_hours = (value / 60) % 24;
+       *out_minutes = value % 60;
+}
+
 static void
 ecep_reminders_alarms_combo_changed_cb (GtkComboBox *combo_box,
                                        ECompEditorPageReminders *page_reminders)
 {
        ECalComponentAlarm *alarm;
        ICalDuration *duration;
-       gint alarm_type;
+       gint alarm_index;
 
        g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
 
@@ -818,13 +846,13 @@ ecep_reminders_alarms_combo_changed_cb (GtkComboBox *combo_box,
        if (!e_comp_editor_page_get_updating (E_COMP_EDITOR_PAGE (page_reminders)))
                e_comp_editor_page_emit_changed (E_COMP_EDITOR_PAGE (page_reminders));
 
-       alarm_type = e_dialog_combo_box_get (page_reminders->priv->alarms_combo, 
page_reminders->priv->alarm_map);
-       if (alarm_type == ALARM_NONE) {
+       alarm_index = ecep_reminders_get_alarm_index (GTK_COMBO_BOX (page_reminders->priv->alarms_combo));
+       if (alarm_index == -1 || alarm_index == 0) {
                e_alarm_list_clear (page_reminders->priv->alarm_list);
                return;
        }
 
-       if (alarm_type == ALARM_CUSTOM) {
+       if (alarm_index == -2) {
                GtkTreeSelection *selection;
 
                selection = gtk_tree_view_get_selection (GTK_TREE_VIEW 
(page_reminders->priv->alarms_tree_view));
@@ -849,37 +877,23 @@ ecep_reminders_alarms_combo_changed_cb (GtkComboBox *combo_box,
 
        i_cal_duration_set_is_neg (duration, TRUE);
 
-       switch (alarm_type) {
-       case ALARM_15_MINUTES:
-               i_cal_duration_set_minutes (duration, 15);
-               break;
+       if (alarm_index >= 1 && alarm_index < N_MAX_PREDEFINED_ALARMS) {
+               gint ii;
 
-       case ALARM_1_HOUR:
-               i_cal_duration_set_hours (duration, 1);
-               break;
+               for (ii = 0; ii < alarm_index - 1 && page_reminders->priv->predefined_alarms[ii] != -1; ii++) 
{
+               }
 
-       case ALARM_1_DAY:
-               i_cal_duration_set_days (duration, 1);
-               break;
+               g_warn_if_fail (ii == alarm_index - 1);
 
-       case ALARM_USER_TIME:
-               switch (page_reminders->priv->alarm_units) {
-               case E_DURATION_DAYS:
-                       i_cal_duration_set_days (duration, page_reminders->priv->alarm_interval);
-                       break;
+               if (ii == alarm_index - 1) {
+                       gint days = 0, hours = 0, minutes = 0;
 
-               case E_DURATION_HOURS:
-                       i_cal_duration_set_hours (duration, page_reminders->priv->alarm_interval);
-                       break;
+                       ecep_reminders_int_to_interval (page_reminders->priv->predefined_alarms[alarm_index - 
1], &days, &hours, &minutes);
 
-               case E_DURATION_MINUTES:
-                       i_cal_duration_set_minutes (duration, page_reminders->priv->alarm_interval);
-                       break;
+                       i_cal_duration_set_days (duration, days);
+                       i_cal_duration_set_hours (duration, hours);
+                       i_cal_duration_set_minutes (duration, minutes);
                }
-               break;
-
-       default:
-               break;
        }
 
        e_cal_component_alarm_take_trigger (alarm,
@@ -1094,11 +1108,10 @@ ecep_reminders_send_to_clicked_cb (GtkWidget *button,
 }
 
 static gboolean
-ecep_reminders_is_custom_alarm (ECalComponentAlarm *ca,
+ecep_reminders_is_custom_alarm (ECompEditorPageReminders *page_reminders,
+                               ECalComponentAlarm *ca,
                                const gchar *old_summary,
-                               EDurationType user_units,
-                               gint user_interval,
-                               gint *alarm_type)
+                               gint *alarm_index)
 {
        ECalComponentAlarmTrigger *trigger;
        ECalComponentAlarmRepeat *repeat;
@@ -1106,6 +1119,7 @@ ecep_reminders_is_custom_alarm (ECalComponentAlarm *ca,
        ECalComponentText *desc;
        ICalDuration *duration;
        GSList *attachments;
+       gint ii, value;
 
        action = e_cal_component_alarm_get_action (ca);
        if (action != E_CAL_COMPONENT_ALARM_DISPLAY)
@@ -1134,70 +1148,23 @@ ecep_reminders_is_custom_alarm (ECalComponentAlarm *ca,
                return TRUE;
 
        duration = e_cal_component_alarm_trigger_get_duration (trigger);
-       if (!duration || i_cal_duration_is_neg (duration) != 1)
-               return TRUE;
-
-       if (i_cal_duration_get_weeks (duration) != 0)
+       if (!duration || (!i_cal_duration_is_neg (duration) && i_cal_duration_as_int (duration) != 0))
                return TRUE;
 
        if (i_cal_duration_get_seconds (duration) != 0)
                return TRUE;
 
-       if (i_cal_duration_get_days (duration) == 1 &&
-           i_cal_duration_get_hours (duration) == 0 &&
-           i_cal_duration_get_minutes (duration) == 0) {
-               if (alarm_type)
-                       *alarm_type = ALARM_1_DAY;
-               return FALSE;
-       }
-
-       if (i_cal_duration_get_days (duration) == 0 &&
-           i_cal_duration_get_hours (duration) == 1 &&
-           i_cal_duration_get_minutes (duration) == 0) {
-               if (alarm_type)
-                       *alarm_type = ALARM_1_HOUR;
-               return FALSE;
-       }
-
-       if (i_cal_duration_get_days (duration) == 0 &&
-           i_cal_duration_get_hours (duration) == 0 &&
-           i_cal_duration_get_minutes (duration) == 15) {
-               if (alarm_type)
-                       *alarm_type = ALARM_15_MINUTES;
-               return FALSE;
-       }
+       value = i_cal_duration_as_int (duration) / 60;
 
-       if (user_interval != -1) {
-               switch (user_units) {
-               case E_DURATION_DAYS:
-                       if (i_cal_duration_get_days (duration) == user_interval &&
-                           i_cal_duration_get_hours (duration) == 0 &&
-                           i_cal_duration_get_minutes (duration) == 0) {
-                               if (alarm_type)
-                                       *alarm_type = ALARM_USER_TIME;
-                               return FALSE;
-                       }
-                       break;
+       if (value < 0)
+               value *= -1;
 
-               case E_DURATION_HOURS:
-                       if (i_cal_duration_get_days (duration) == 0 &&
-                           i_cal_duration_get_hours (duration) == user_interval &&
-                           i_cal_duration_get_minutes (duration) == 0) {
-                               if (alarm_type)
-                                       *alarm_type = ALARM_USER_TIME;
-                               return FALSE;
-                       }
-                       break;
+       for (ii = 0; ii < N_MAX_PREDEFINED_ALARMS && page_reminders->priv->predefined_alarms[ii] != -1; ii++) 
{
+               if (value == page_reminders->priv->predefined_alarms[ii]) {
+                       if (alarm_index)
+                               *alarm_index = ii + 1;
 
-               case E_DURATION_MINUTES:
-                       if (i_cal_duration_get_days (duration) == 0 &&
-                           i_cal_duration_get_hours (duration) == 0 &&
-                           i_cal_duration_get_minutes (duration) == user_interval) {
-                               if (alarm_type)
-                                       *alarm_type = ALARM_USER_TIME;
-                               return FALSE;
-                       }
-                       break;
+                       return FALSE;
                }
        }
 
@@ -1205,12 +1172,11 @@ ecep_reminders_is_custom_alarm (ECalComponentAlarm *ca,
 }
 
 static gboolean
-ecep_reminders_is_custom_alarm_uid_list (ECalComponent *comp,
+ecep_reminders_is_custom_alarm_uid_list (ECompEditorPageReminders *page_reminders,
+                                        ECalComponent *comp,
                                         GSList *alarm_uids,
                                         const gchar *old_summary,
-                                        EDurationType user_units,
-                                        gint user_interval,
-                                        gint *alarm_type)
+                                        gint *alarm_index)
 {
        ECalComponentAlarm *ca;
        gboolean result;
@@ -1222,7 +1188,7 @@ ecep_reminders_is_custom_alarm_uid_list (ECalComponent *comp,
                return TRUE;
 
        ca = e_cal_component_get_alarm (comp, alarm_uids->data);
-       result = ecep_reminders_is_custom_alarm (ca, old_summary, user_units, user_interval, alarm_type);
+       result = ecep_reminders_is_custom_alarm (page_reminders, ca, old_summary, alarm_index);
        e_cal_component_alarm_free (ca);
 
        return result;
@@ -1406,7 +1372,7 @@ ecep_reminders_fill_widgets (ECompEditorPage *page,
 
        valarm = i_cal_component_get_first_component (component, I_CAL_VALARM_COMPONENT);
        if (!valarm) {
-               e_dialog_combo_box_set (page_reminders->priv->alarms_combo, ALARM_NONE, 
page_reminders->priv->alarm_map);
+               gtk_combo_box_set_active (GTK_COMBO_BOX (page_reminders->priv->alarms_combo), 0);
                return;
        }
 
@@ -1415,15 +1381,21 @@ ecep_reminders_fill_widgets (ECompEditorPage *page,
        comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (component));
        if (comp && e_cal_component_has_alarms (comp)) {
                GSList *alarms, *link;
-               gint alarm_type = ALARM_NONE;
+               gint alarm_index = 0;
 
                alarms = e_cal_component_get_alarm_uids (comp);
 
-               if (ecep_reminders_is_custom_alarm_uid_list (comp, alarms, i_cal_component_get_summary 
(component),
-                       page_reminders->priv->alarm_units, page_reminders->priv->alarm_interval, &alarm_type))
-                       alarm_type = ALARM_CUSTOM;
+               if (ecep_reminders_is_custom_alarm_uid_list (page_reminders, comp, alarms, 
i_cal_component_get_summary (component), &alarm_index)) {
+                       GtkTreeModel *model;
 
-               e_dialog_combo_box_set (page_reminders->priv->alarms_combo, alarm_type, 
page_reminders->priv->alarm_map);
+                       model = gtk_combo_box_get_model (GTK_COMBO_BOX (page_reminders->priv->alarms_combo));
+                       alarm_index = gtk_tree_model_iter_n_children (model, NULL) - 1;
+               }
+
+               if (alarm_index < 0)
+                       alarm_index = 0;
+
+               gtk_combo_box_set_active (GTK_COMBO_BOX (page_reminders->priv->alarms_combo), alarm_index);
 
                e_alarm_list_clear (page_reminders->priv->alarm_list);
 
@@ -1438,7 +1410,7 @@ ecep_reminders_fill_widgets (ECompEditorPage *page,
 
                g_slist_free_full (alarms, g_free);
 
-               if (e_dialog_combo_box_get (page_reminders->priv->alarms_combo, 
page_reminders->priv->alarm_map) == ALARM_CUSTOM) {
+               if (ecep_reminders_get_alarm_index (GTK_COMBO_BOX (page_reminders->priv->alarms_combo)) == 
-2) {
                        GtkTreeSelection *selection;
                        GtkTreeIter iter;
 
@@ -1447,7 +1419,7 @@ ecep_reminders_fill_widgets (ECompEditorPage *page,
                                gtk_tree_selection_select_iter (selection, &iter);
                }
        } else {
-               e_dialog_combo_box_set (page_reminders->priv->alarms_combo, ALARM_NONE, 
page_reminders->priv->alarm_map);
+               gtk_combo_box_set_active (GTK_COMBO_BOX (page_reminders->priv->alarms_combo), 0);
        }
 
        g_clear_object (&comp);
@@ -1642,6 +1614,353 @@ ecep_reminders_setup_ui (ECompEditorPageReminders *page_reminders)
        }
 }
 
+static gboolean
+ecep_reminders_add_predefined_alarm (ECompEditorPageReminders *page_reminders,
+                                    gint value_minutes)
+{
+       gint ii;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders), FALSE);
+       g_return_val_if_fail (value_minutes >= 0, FALSE);
+
+       for (ii = 0; ii < N_MAX_PREDEFINED_ALARMS && page_reminders->priv->predefined_alarms[ii] != -1; ii++) 
{
+               if (value_minutes == page_reminders->priv->predefined_alarms[ii])
+                       return FALSE;
+       }
+
+       if (ii < N_MAX_PREDEFINED_ALARMS) {
+               page_reminders->priv->predefined_alarms[ii] = value_minutes;
+
+               if (ii + 1 < N_MAX_PREDEFINED_ALARMS)
+                       page_reminders->priv->predefined_alarms[ii + 1] = -1;
+       }
+
+       return ii < N_MAX_PREDEFINED_ALARMS;
+}
+
+static gint
+ecep_reminders_compare_predefined_alarm (gconstpointer data1,
+                                        gconstpointer data2,
+                                        gpointer user_data)
+{
+       gint value1 = * (gint *) data1;
+       gint value2 = * (gint *) data2;
+
+       return value1 - value2;
+}
+
+static void
+ecep_reminders_sort_predefined_alarms (ECompEditorPageReminders *page_reminders)
+{
+       gint nelems;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       for (nelems = N_PREDEFINED_ALARMS; nelems < N_MAX_PREDEFINED_ALARMS && 
page_reminders->priv->predefined_alarms[nelems] != -1; nelems++) {
+               /* Just count those filled */
+       }
+
+       nelems -= N_PREDEFINED_ALARMS;
+
+       if (nelems > 1) {
+               g_qsort_with_data (&(page_reminders->priv->predefined_alarms[N_PREDEFINED_ALARMS]), nelems,
+                       sizeof (gint), ecep_reminders_compare_predefined_alarm, NULL);
+       }
+}
+
+static gboolean
+ecep_reminders_fill_alarms_combo (ECompEditorPageReminders *page_reminders,
+                                 gint select_minutes)
+{
+       GtkComboBoxText *text_combo;
+       gint ii, select_index = 0;
+       gboolean did_select = FALSE;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders), FALSE);
+       g_return_val_if_fail (GTK_IS_COMBO_BOX_TEXT (page_reminders->priv->alarms_combo), FALSE);
+
+       text_combo = GTK_COMBO_BOX_TEXT (page_reminders->priv->alarms_combo);
+
+       g_signal_handlers_block_by_func (text_combo, ecep_reminders_alarms_combo_changed_cb, page_reminders);
+
+       if (select_minutes < 0)
+               select_index = gtk_combo_box_get_active (GTK_COMBO_BOX (text_combo));
+
+       gtk_combo_box_text_remove_all (text_combo);
+
+       /* Translators: "None" for "No reminder set" */
+       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "None"));
+
+       for (ii = 0; ii < N_MAX_PREDEFINED_ALARMS && page_reminders->priv->predefined_alarms[ii] != -1; ii++) 
{
+               gchar *text, *merged;
+
+               if (page_reminders->priv->predefined_alarms[ii]) {
+                       text = e_cal_util_seconds_to_string (page_reminders->priv->predefined_alarms[ii] * 
60);
+                       /* Translators: This constructs predefined reminder's description, for example "15 
minutes before",
+                          "1 hour before", "1 day before", but, if user has set, also more complicated 
strings like
+                          "2 days 13 hours 1 minute before". */
+                       merged = g_strdup_printf (C_("cal-reminders", "%s before"), text);
+                       gtk_combo_box_text_append_text (text_combo, merged);
+                       g_free (merged);
+                       g_free (text);
+               } else {
+                       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "at the start"));
+               }
+
+               if (select_minutes >= 0 && select_minutes == page_reminders->priv->predefined_alarms[ii])
+                       select_index = ii + 1;
+       }
+
+       /* Translators: "Custom" for "Custom reminder set" */
+       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "Custom"));
+
+       g_signal_handlers_unblock_by_func (text_combo, ecep_reminders_alarms_combo_changed_cb, 
page_reminders);
+
+       if (select_index >= 0 && select_index <= ii) {
+               gtk_combo_box_set_active (GTK_COMBO_BOX (text_combo), select_index);
+               did_select = select_minutes >= 0;
+       } else {
+               gtk_combo_box_set_active (GTK_COMBO_BOX (text_combo), 0);
+       }
+
+       return did_select;
+}
+
+static void
+ecep_reminders_add_default_alarm_time (ECompEditorPageReminders *page_reminders)
+{
+       EDurationType alarm_units;
+       gint alarm_interval, minutes;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       alarm_interval = calendar_config_get_default_reminder_interval ();
+       alarm_units = calendar_config_get_default_reminder_units ();
+
+       minutes = ecep_reminders_interval_to_int (
+               alarm_units == E_DURATION_DAYS ? alarm_interval : 0,
+               alarm_units == E_DURATION_HOURS ? alarm_interval : 0,
+               alarm_units == E_DURATION_MINUTES ? alarm_interval : 0);
+
+       ecep_reminders_add_predefined_alarm (page_reminders, minutes);
+}
+
+static void
+ecep_reminders_add_custom_time_add_button_clicked_cb (GtkButton *button,
+                                                     gpointer user_data)
+{
+       ECompEditorPageReminders *page_reminders = user_data;
+       gboolean found = FALSE;
+       gint new_minutes, ii;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       new_minutes = ecep_reminders_interval_to_int (
+               gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON 
(page_reminders->priv->add_custom_time_days_spin)),
+               gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON 
(page_reminders->priv->add_custom_time_hours_spin)),
+               gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON 
(page_reminders->priv->add_custom_time_minutes_spin)));
+       g_return_if_fail (new_minutes >= 0);
+
+       gtk_widget_hide (page_reminders->priv->add_custom_time_popover);
+
+       for (ii = 0; ii < N_MAX_PREDEFINED_ALARMS && page_reminders->priv->predefined_alarms[ii] != -1; ii++) 
{
+               if (new_minutes == page_reminders->priv->predefined_alarms[ii]) {
+                       found = TRUE;
+                       gtk_combo_box_set_active (GTK_COMBO_BOX (page_reminders->priv->alarms_combo), ii + 1);
+                       break;
+               }
+       }
+
+       if (!found) {
+               GSettings *settings;
+               GVariant *variant;
+               gboolean any_user_alarm_added = FALSE;
+               gint32 array[N_MAX_PREDEFINED_USER_ALARMS + 1] = { 0 }, narray = 0, ii;
+
+               settings = e_util_ref_settings ("org.gnome.evolution.calendar");
+               variant = g_settings_get_value (settings, "custom-reminders-minutes");
+               if (variant) {
+                       const gint32 *stored;
+                       gsize nstored = 0;
+
+                       stored = g_variant_get_fixed_array (variant, &nstored, sizeof (gint32));
+                       if (stored && nstored > 0) {
+                               /* Skip the oldest, when too many stored */
+                               for (ii = nstored >= N_MAX_PREDEFINED_USER_ALARMS ? 1 : 0; ii < 
N_MAX_PREDEFINED_USER_ALARMS && ii < nstored; ii++) {
+                                       array[narray] = stored[ii];
+                                       narray++;
+                               }
+                       }
+
+                       g_variant_unref (variant);
+               }
+
+               /* Add the new at the end of the array */
+               array[narray] = new_minutes;
+               narray++;
+
+               variant = g_variant_new_fixed_array (G_VARIANT_TYPE_INT32, array, narray, sizeof (gint32));
+               g_settings_set_value (settings, "custom-reminders-minutes", variant);
+
+               g_object_unref (settings);
+
+               page_reminders->priv->predefined_alarms[N_PREDEFINED_ALARMS] = -1;
+
+               ecep_reminders_add_default_alarm_time (page_reminders);
+
+               for (ii = 0; ii < narray; ii++) {
+                       if (ecep_reminders_add_predefined_alarm (page_reminders, array[ii]))
+                               any_user_alarm_added = TRUE;
+               }
+
+               ecep_reminders_sort_predefined_alarms (page_reminders);
+
+               if (!ecep_reminders_fill_alarms_combo (page_reminders, new_minutes))
+                       gtk_combo_box_set_active (GTK_COMBO_BOX (page_reminders->priv->alarms_combo), 0);
+
+               gtk_widget_set_sensitive (page_reminders->priv->remove_custom_times_button, 
any_user_alarm_added);
+       }
+}
+
+static void
+ecep_reminders_add_custom_time_clicked_cb (GtkWidget *button,
+                                          gpointer user_data)
+{
+       ECompEditorPageReminders *page_reminders = user_data;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       if (!page_reminders->priv->add_custom_time_popover) {
+               GtkWidget *widget;
+               GtkBox *vbox, *box;
+
+               page_reminders->priv->add_custom_time_days_spin = gtk_spin_button_new_with_range (0.0, 366.0, 
1.0);
+               page_reminders->priv->add_custom_time_hours_spin = gtk_spin_button_new_with_range (0.0, 23.0, 
1.0);
+               page_reminders->priv->add_custom_time_minutes_spin = gtk_spin_button_new_with_range (0.0, 
59.0, 1.0);
+
+               g_object_set (G_OBJECT (page_reminders->priv->add_custom_time_days_spin),
+                       "digits", 0,
+                       "numeric", TRUE,
+                       "snap-to-ticks", TRUE,
+                       NULL);
+
+               g_object_set (G_OBJECT (page_reminders->priv->add_custom_time_hours_spin),
+                       "digits", 0,
+                       "numeric", TRUE,
+                       "snap-to-ticks", TRUE,
+                       NULL);
+
+               g_object_set (G_OBJECT (page_reminders->priv->add_custom_time_minutes_spin),
+                       "digits", 0,
+                       "numeric", TRUE,
+                       "snap-to-ticks", TRUE,
+                       NULL);
+
+               vbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 2));
+
+               widget = gtk_label_new (_("Set a custom predefined time to"));
+               gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0);
+
+               box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2));
+               g_object_set (G_OBJECT (box),
+                       "halign", GTK_ALIGN_START,
+                       "hexpand", FALSE,
+                       "valign", GTK_ALIGN_CENTER,
+                       "vexpand", FALSE,
+                       NULL);
+
+               gtk_box_pack_start (box, page_reminders->priv->add_custom_time_days_spin, FALSE, FALSE, 4);
+               /* Translators: this is part of: "Set a custom predefined time to [nnn] days [nnn] hours 
[nnn] minutes", where the text in "[]" means a separate widget */
+               widget = gtk_label_new_with_mnemonic (C_("cal-reminders", "da_ys"));
+               gtk_label_set_mnemonic_widget (GTK_LABEL (widget), 
page_reminders->priv->add_custom_time_days_spin);
+               gtk_box_pack_start (box, widget, FALSE, FALSE, 4);
+
+               gtk_box_pack_start (vbox, GTK_WIDGET (box), FALSE, FALSE, 0);
+
+               box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2));
+               g_object_set (G_OBJECT (box),
+                       "halign", GTK_ALIGN_START,
+                       "hexpand", FALSE,
+                       "valign", GTK_ALIGN_CENTER,
+                       "vexpand", FALSE,
+                       NULL);
+
+               gtk_box_pack_start (box, page_reminders->priv->add_custom_time_hours_spin, FALSE, FALSE, 4);
+               /* Translators: this is part of: "Set a custom predefined time to [nnn] days [nnn] hours 
[nnn] minutes", where the text in "[]" means a separate widget */
+               widget = gtk_label_new_with_mnemonic (C_("cal-reminders", "_hours"));
+               gtk_label_set_mnemonic_widget (GTK_LABEL (widget), 
page_reminders->priv->add_custom_time_hours_spin);
+               gtk_box_pack_start (box, widget, FALSE, FALSE, 4);
+
+               gtk_box_pack_start (vbox, GTK_WIDGET (box), FALSE, FALSE, 0);
+
+               box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2));
+               g_object_set (G_OBJECT (box),
+                       "halign", GTK_ALIGN_START,
+                       "hexpand", FALSE,
+                       "valign", GTK_ALIGN_CENTER,
+                       "vexpand", FALSE,
+                       NULL);
+
+               gtk_box_pack_start (box, page_reminders->priv->add_custom_time_minutes_spin, FALSE, FALSE, 4);
+               /* Translators: this is part of: "Set a custom predefined time to [nnn] days [nnn] hours 
[nnn] minutes", where the text in "[]" means a separate widget */
+               widget = gtk_label_new_with_mnemonic (C_("cal-reminders", "_minutes"));
+               gtk_label_set_mnemonic_widget (GTK_LABEL (widget), 
page_reminders->priv->add_custom_time_minutes_spin);
+               gtk_box_pack_start (box, widget, FALSE, FALSE, 4);
+
+               gtk_box_pack_start (vbox, GTK_WIDGET (box), FALSE, FALSE, 0);
+
+               page_reminders->priv->add_custom_time_add_button = gtk_button_new_with_mnemonic (_("_Add 
time"));
+               g_object_set (G_OBJECT (page_reminders->priv->add_custom_time_add_button),
+                       "halign", GTK_ALIGN_CENTER,
+                       NULL);
+
+               gtk_box_pack_start (vbox, page_reminders->priv->add_custom_time_add_button, FALSE, FALSE, 0);
+
+               gtk_widget_show_all (GTK_WIDGET (vbox));
+
+               page_reminders->priv->add_custom_time_popover = gtk_popover_new (GTK_WIDGET (page_reminders));
+               gtk_popover_set_position (GTK_POPOVER (page_reminders->priv->add_custom_time_popover), 
GTK_POS_BOTTOM);
+               gtk_container_add (GTK_CONTAINER (page_reminders->priv->add_custom_time_popover), GTK_WIDGET 
(vbox));
+               gtk_container_set_border_width (GTK_CONTAINER 
(page_reminders->priv->add_custom_time_popover), 6);
+
+               g_signal_connect (page_reminders->priv->add_custom_time_add_button, "clicked",
+                       G_CALLBACK (ecep_reminders_add_custom_time_add_button_clicked_cb), page_reminders);
+       }
+
+       gtk_widget_hide (page_reminders->priv->add_custom_time_popover);
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->add_custom_time_days_spin), 0.0);
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->add_custom_time_hours_spin), 0.0);
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->add_custom_time_minutes_spin), 0.0);
+       gtk_popover_set_relative_to (GTK_POPOVER (page_reminders->priv->add_custom_time_popover), button);
+       gtk_widget_show (page_reminders->priv->add_custom_time_popover);
+
+       gtk_widget_grab_focus (page_reminders->priv->add_custom_time_days_spin);
+}
+
+static void
+ecep_reminders_remove_custom_times_clicked_cb (GtkButton *button,
+                                              gpointer user_data)
+{
+       ECompEditorPageReminders *page_reminders = user_data;
+       GSettings *settings;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       settings = e_util_ref_settings ("org.gnome.evolution.calendar");
+       g_settings_reset (settings, "custom-reminders-minutes");
+       g_object_unref (settings);
+
+       page_reminders->priv->predefined_alarms[N_PREDEFINED_ALARMS] = -1;
+
+       ecep_reminders_add_default_alarm_time (page_reminders);
+
+       ecep_reminders_fill_alarms_combo (page_reminders, -1);
+
+       gtk_combo_box_set_active (GTK_COMBO_BOX (page_reminders->priv->alarms_combo), 0);
+
+       gtk_widget_set_sensitive (page_reminders->priv->remove_custom_times_button, FALSE);
+}
+
 static void
 ecep_reminders_constructed (GObject *object)
 {
@@ -1655,7 +1974,10 @@ ecep_reminders_constructed (GObject *object)
        GtkGrid *grid;
        ECompEditor *comp_editor;
        EFocusTracker *focus_tracker;
-       gchar *combo_label, *config_dir;
+       gint ii;
+       gchar *config_dir;
+       GSettings *settings;
+       GVariant *variant;
 
        G_OBJECT_CLASS (e_comp_editor_page_reminders_parent_class)->constructed (object);
 
@@ -1716,58 +2038,60 @@ ecep_reminders_constructed (GObject *object)
 
        gtk_label_set_mnemonic_widget (GTK_LABEL (label), page_reminders->priv->alarms_combo);
 
-       /* Add the user defined time if necessary */
-       page_reminders->priv->alarm_interval = calendar_config_get_default_reminder_interval ();
-       page_reminders->priv->alarm_units = calendar_config_get_default_reminder_units ();
+       widget = e_dialog_button_new_with_icon ("list-add", NULL);
+       gtk_widget_set_tooltip_text (widget, _("Add custom predefined time"));
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
 
-       combo_label = NULL;
+       g_signal_connect (widget, "clicked",
+               G_CALLBACK (ecep_reminders_add_custom_time_clicked_cb), page_reminders);
 
-       switch (page_reminders->priv->alarm_units) {
-       case E_DURATION_DAYS:
-               if (page_reminders->priv->alarm_interval != 1) {
-                       combo_label = g_strdup_printf (ngettext ("%d day before", "%d days before",
-                               page_reminders->priv->alarm_interval), page_reminders->priv->alarm_interval);
-               }
-               break;
+       widget = e_dialog_button_new_with_icon ("edit-clear", NULL);
+       gtk_widget_set_tooltip_text (widget, _("Remove custom predefined times"));
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_reminders->priv->remove_custom_times_button = widget;
+       gtk_widget_set_sensitive (page_reminders->priv->remove_custom_times_button, FALSE);
 
-       case E_DURATION_HOURS:
-               if (page_reminders->priv->alarm_interval != 1) {
-                       combo_label = g_strdup_printf (ngettext ("%d hour before", "%d hours before",
-                               page_reminders->priv->alarm_interval), page_reminders->priv->alarm_interval);
-               }
-               break;
+       g_signal_connect (widget, "clicked",
+               G_CALLBACK (ecep_reminders_remove_custom_times_clicked_cb), page_reminders);
+
+       page_reminders->priv->predefined_alarms[0] = ecep_reminders_interval_to_int (0, 0, 15);
+       page_reminders->priv->predefined_alarms[1] = ecep_reminders_interval_to_int (0, 1, 0);
+       page_reminders->priv->predefined_alarms[2] = ecep_reminders_interval_to_int (1, 0, 0);
+       page_reminders->priv->predefined_alarms[3] = -1;
+
+       ecep_reminders_add_default_alarm_time (page_reminders);
+
+       settings = e_util_ref_settings ("org.gnome.evolution.calendar");
+       variant = g_settings_get_value (settings, "custom-reminders-minutes");
+
+       if (variant) {
+               const gint32 *stored;
+               gsize nstored = 0;
+
+               stored = g_variant_get_fixed_array (variant, &nstored, sizeof (gint32));
+               if (stored && nstored > 0) {
+                       if (nstored > N_MAX_PREDEFINED_USER_ALARMS)
+                               nstored = N_MAX_PREDEFINED_USER_ALARMS;
 
-       case E_DURATION_MINUTES:
-               if (page_reminders->priv->alarm_interval != 15) {
-                       combo_label = g_strdup_printf (ngettext ("%d minute before", "%d minutes before",
-                               page_reminders->priv->alarm_interval), page_reminders->priv->alarm_interval);
+                       for (ii = 0; ii < nstored; ii++) {
+                               if (stored[ii] >= 0 &&
+                                   ecep_reminders_add_predefined_alarm (page_reminders, stored[ii])) {
+                                       gtk_widget_set_sensitive 
(page_reminders->priv->remove_custom_times_button, TRUE);
+                               }
+                       }
                }
-               break;
-       }
 
-       text_combo = GTK_COMBO_BOX_TEXT (widget);
-       /* Translators: "None" for "No reminder set" */
-       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "None"));
-        /* Translators: Predefined reminder's description */
-       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "15 minutes before"));
-        /* Translators: Predefined reminder's description */
-       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "1 hour before"));
-        /* Translators: Predefined reminder's description */
-       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "1 day before"));
-
-       if (combo_label) {
-               gtk_combo_box_text_append_text (text_combo, combo_label);
-               g_free (combo_label);
-
-               page_reminders->priv->alarm_map = alarm_map_with_user_time;
-       } else {
-               page_reminders->priv->alarm_map = alarm_map_without_user_time;
+               g_variant_unref (variant);
        }
 
-       /* Translators: "Custom" for "Custom reminder set" */
-       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "Custom"));
+       g_object_unref (settings);
 
-       gtk_combo_box_set_active (GTK_COMBO_BOX (text_combo), 0);
+       ecep_reminders_sort_predefined_alarms (page_reminders);
+       ecep_reminders_fill_alarms_combo (page_reminders, -1);
+
+       gtk_combo_box_set_active (GTK_COMBO_BOX (page_reminders->priv->alarms_combo), 0);
 
        g_signal_connect (page_reminders->priv->alarms_combo, "changed",
                G_CALLBACK (ecep_reminders_alarms_combo_changed_cb), page_reminders);


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