[gnome-calendar/wip/gbsneto/quick-add-popover: 2/3] quick-add-popover: new class to handle new events



commit 11070f1d1725019fe68fd1b2cf28aecc87411cf4
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Tue Feb 16 13:32:35 2016 -0200

    quick-add-popover: new class to handle new events
    
    Instead of throwing it all for GcalWindow to handle,
    add a new class called GcalQuickAddPopover that handles
    event creation.
    
    Events are always all day when created from the quick
    add popover, unless the user wants to change it and click
    'Edit Details' button.
    
    In the future, we'll look forward adding natural language
    support, so the user can type the times directly from the
    entry (like e.g. "soccer at park from 10am to 11:30am").

 data/Makefile.am             |    1 +
 data/calendar.gresource.xml  |    1 +
 data/ui/quick-add-popover.ui |  244 ++++++++++++++
 src/Makefile.am              |    2 +
 src/gcal-quick-add-popover.c |  746 ++++++++++++++++++++++++++++++++++++++++++
 src/gcal-quick-add-popover.h |   51 +++
 6 files changed, 1045 insertions(+), 0 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index 577c312..48c2837 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -53,6 +53,7 @@ EXTRA_DIST=                     \
   ui/date-selector.ui           \
   ui/edit-dialog.ui             \
   ui/menus.ui                   \
+  ui/quick-add-popover.ui       \
   ui/search-view.ui             \
   ui/source-dialog.ui           \
   ui/time-selector.ui           \
diff --git a/data/calendar.gresource.xml b/data/calendar.gresource.xml
index 7a150e2..70a5116 100644
--- a/data/calendar.gresource.xml
+++ b/data/calendar.gresource.xml
@@ -6,6 +6,7 @@
     <file alias="edit-dialog.ui" compressed="true" preprocess="xml-stripblanks">ui/edit-dialog.ui</file>
     <file alias="menus.ui" compressed="true" preprocess="xml-stripblanks">ui/menus.ui</file>
     <file alias="online-account-row.ui" compressed="true" 
preprocess="xml-stripblanks">ui/online-account-row.ui</file>
+    <file alias="quick-add-popover.ui" compressed="true" 
preprocess="xml-stripblanks">ui/quick-add-popover.ui</file>
     <file alias="search-view.ui" compressed="true" preprocess="xml-stripblanks">ui/search-view.ui</file>
     <file alias="source-dialog.ui" compressed="true" preprocess="xml-stripblanks">ui/source-dialog.ui</file>
     <file alias="time-selector.ui" compressed="true" preprocess="xml-stripblanks">ui/time-selector.ui</file>
diff --git a/data/ui/quick-add-popover.ui b/data/ui/quick-add-popover.ui
new file mode 100644
index 0000000..f52a659
--- /dev/null
+++ b/data/ui/quick-add-popover.ui
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.16"/>
+  <template class="GcalQuickAddPopover" parent="GtkPopover">
+    <property name="width_request">350</property>
+    <property name="can_focus">False</property>
+    <child>
+      <object class="GtkStack" id="stack">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="hexpand">True</property>
+        <property name="vexpand">True</property>
+        <property name="border_width">12</property>
+        <property name="hhomogeneous">False</property>
+        <property name="transition_type">slide-left-right</property>
+        <property name="interpolate_size">True</property>
+        <child>
+          <object class="GtkGrid">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="row_spacing">12</property>
+            <property name="column_spacing">12</property>
+            <child>
+              <object class="GtkLabel" id="title_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="hexpand">True</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+                <property name="width">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="summary_entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="hexpand">True</property>
+                <signal name="notify::text" handler="summary_entry_text_changed" 
object="GcalQuickAddPopover" swapped="no" />
+                <signal name="activate" handler="summary_entry_activated" object="GcalQuickAddPopover" 
swapped="no" />
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">1</property>
+                <property name="width">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="select_calendar_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hexpand">True</property>
+                <signal name="clicked" handler="select_calendar_button_clicked" object="GcalQuickAddPopover" 
swapped="yes" />
+                <child>
+                  <object class="GtkBox">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkImage" id="color_image">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="stock">gtk-missing-image</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="calendar_name_label">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="hexpand">True</property>
+                        <property name="xalign">0</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="icon_name">go-next-symbolic</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">2</property>
+                <property name="width">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="edit_button">
+                <property name="label" translatable="yes">Edit Details…</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="margin_top">6</property>
+                <signal name="clicked" handler="edit_or_create_event" object="GcalQuickAddPopover" 
swapped="yes" />
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="add_button">
+                <property name="label" translatable="yes">Add</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="sensitive">False</property>
+                <property name="margin_top">6</property>
+                <signal name="clicked" handler="edit_or_create_event" object="GcalQuickAddPopover" 
swapped="yes" />
+                <style>
+                  <class name="suggested-action"/>
+                </style>
+              </object>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="top_attach">3</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="name">events</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkButton" id="back_button">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="relief">none</property>
+                    <signal name="clicked" handler="back_button_clicked" object="GcalQuickAddPopover" 
swapped="yes" />
+                    <style>
+                      <class name="image-button"/>
+                    </style>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="icon_name">go-previous-symbolic</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child type="center">
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Calendar</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkScrolledWindow">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="hexpand">True</property>
+                <property name="vexpand">True</property>
+                <property name="shadow_type">in</property>
+                <child>
+                  <object class="GtkViewport">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <object class="GtkListBox" id="calendars_listbox">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="selection_mode">none</property>
+                        <signal name="row-activated" handler="select_row" object="GcalQuickAddPopover" 
swapped="yes" />
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="name">calendars</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkSizeGroup">
+    <widgets>
+      <widget name="edit_button"/>
+      <widget name="add_button"/>
+    </widgets>
+  </object>
+</interface>
diff --git a/src/Makefile.am b/src/Makefile.am
index d465ece..597184d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -42,6 +42,8 @@ gnome_calendar_SOURCES =                                  \
     gcal-manager.h                                        \
     gcal-month-view.c                                     \
     gcal-month-view.h                                     \
+    gcal-quick-add-popover.c                              \
+    gcal-quick-add-popover.h                              \
     gcal-search-view.h                                    \
     gcal-search-view.c                                    \
     gcal-shell-search-provider.h                          \
diff --git a/src/gcal-quick-add-popover.c b/src/gcal-quick-add-popover.c
new file mode 100644
index 0000000..7f9aad2
--- /dev/null
+++ b/src/gcal-quick-add-popover.c
@@ -0,0 +1,746 @@
+/* gcal-quick-add-popover.c
+ *
+ * Copyright (C) 2016 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "gcal-quick-add-popover.h"
+#include "gcal-utils.h"
+
+struct _GcalQuickAddPopover
+{
+  GtkPopover          parent;
+
+  GtkWidget          *add_button;
+  GtkWidget          *calendars_listbox;
+  GtkWidget          *calendar_name_label;
+  GtkWidget          *color_image;
+  GtkWidget          *stack;
+  GtkWidget          *summary_entry;
+  GtkWidget          *title_label;
+
+  /* Internal data */
+  GDateTime          *date_start;
+  GDateTime          *date_end;
+
+  GtkWidget          *selected_row;
+
+  GcalManager        *manager;
+};
+
+G_DEFINE_TYPE (GcalQuickAddPopover, gcal_quick_add_popover, GTK_TYPE_POPOVER)
+
+enum {
+  PROP_0,
+  PROP_DATE_START,
+  PROP_DATE_END,
+  PROP_MANAGER,
+  N_PROPS
+};
+
+enum
+{
+  EDIT_EVENT,
+  NUM_SIGNALS
+};
+
+static guint signals[NUM_SIGNALS] = { 0, };
+
+/*
+ * Utility functions
+ */
+
+static GtkWidget*
+create_row_for_source (GcalManager *manager,
+                       ESource     *source,
+                       gboolean     writable)
+{
+  GtkWidget *row, *box, *icon, *label, *selected_icon;
+  GdkPixbuf *circle_pixbuf;
+  GdkRGBA color;
+  gchar *tooltip, *parent_name;
+
+  /* The main box */
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+  gtk_widget_set_margin_start (box, 6);
+  gtk_widget_set_margin_end (box, 6);
+  gtk_widget_set_margin_top (box, 3);
+  gtk_widget_set_margin_bottom (box, 3);
+
+  /* The icon with the source color */
+  get_color_name_from_source (source, &color);
+  circle_pixbuf = get_circle_pixbuf_from_color (&color, 16);
+  icon = gtk_image_new_from_pixbuf (circle_pixbuf);
+
+  gtk_container_add (GTK_CONTAINER (box), icon);
+
+  /* Source name label */
+  label = gtk_label_new (e_source_get_display_name (source));
+  gtk_widget_set_margin_end (label, 12);
+
+  gtk_container_add (GTK_CONTAINER (box), label);
+
+  /* Selected icon */
+  selected_icon = gtk_image_new_from_icon_name ("emblem-ok-symbolic", GTK_ICON_SIZE_BUTTON);
+  gtk_container_add (GTK_CONTAINER (box), selected_icon);
+
+  /* The row itself */
+  row = gtk_list_box_row_new ();
+  gtk_widget_set_sensitive (row, writable);
+
+  gtk_container_add (GTK_CONTAINER (row), box);
+
+  /* Setup also a cool tooltip */
+  get_source_parent_name_color (manager, source, &parent_name, NULL);
+
+  if (writable)
+    tooltip = g_strdup (parent_name);
+  else
+    tooltip = g_strdup_printf (_("%s (this calendar is read-only)"), parent_name);
+
+  gtk_widget_set_tooltip_text (row, tooltip);
+
+  /*
+   * Setup the row data. A reference for the source is
+   * taken to make sure it is not destroyed before this
+   * row.
+   */
+  g_object_set_data_full (G_OBJECT (row), "source", g_object_ref (source), g_object_unref);
+  g_object_set_data (G_OBJECT (row), "selected-icon", selected_icon);
+  g_object_set_data (G_OBJECT (row), "color-icon", icon);
+  g_object_set_data (G_OBJECT (row), "name-label", label);
+
+  gtk_widget_show (label);
+  gtk_widget_show (icon);
+  gtk_widget_show (box);
+  gtk_widget_show (row);
+
+  g_clear_object (&circle_pixbuf);
+  g_free (parent_name);
+  g_free (tooltip);
+
+  return row;
+}
+
+/*
+ * Retrieves the corresponding row for the given
+ * ESource from the listbox.
+ */
+static GtkWidget*
+get_row_for_source (GcalQuickAddPopover *self,
+                    ESource             *source)
+{
+  GList *children, *l;
+  GtkWidget *row;
+
+  row = NULL;
+  children = gtk_container_get_children (GTK_CONTAINER (self->calendars_listbox));
+
+  for (l = children; l != NULL; l = g_list_next (l))
+    {
+      ESource *row_source = g_object_get_data (l->data, "source");
+
+      if (row_source == source)
+        {
+          row = l->data;
+          break;
+        }
+    }
+
+  g_list_free (children);
+
+  return row;
+}
+
+static void
+select_row (GcalQuickAddPopover *self,
+            GtkListBoxRow       *row)
+{
+  GtkWidget *icon;
+  GdkPixbuf *circle_pixbuf;
+  ESource *source;
+  GdkRGBA color;
+
+  /* First, unselect the previous row */
+  if (self->selected_row)
+    {
+      icon = g_object_get_data (G_OBJECT (self->selected_row), "selected-icon");
+      gtk_widget_set_visible (icon, FALSE);
+    }
+
+  /* Now, select and save the new row */
+  icon = g_object_get_data (G_OBJECT (row), "selected-icon");
+  gtk_widget_set_visible (icon, TRUE);
+
+  self->selected_row = GTK_WIDGET (row);
+
+  /* Setup the event page's source name and color */
+  source = g_object_get_data (G_OBJECT (row), "source");
+
+  gtk_label_set_label (GTK_LABEL (self->calendar_name_label), e_source_get_display_name (source));
+
+  get_color_name_from_source (source, &color);
+  circle_pixbuf = get_circle_pixbuf_from_color (&color, 16);
+  gtk_image_set_from_pixbuf (GTK_IMAGE (self->color_image), circle_pixbuf);
+
+  /* Return to the events page */
+  gtk_stack_set_visible_child_name (GTK_STACK (self->stack), "events");
+
+  g_clear_object (&circle_pixbuf);
+}
+
+static void
+update_header (GcalQuickAddPopover *self)
+{
+  icaltimetype *dtstart;
+  struct tm stm_date, etm_date;
+  gboolean all_day;
+  gchar start[64], end[64];
+  gchar *title_date;
+
+  all_day = datetime_is_date (self->date_start) && (self->date_end ? datetime_is_date (self->date_end) : 
TRUE);
+  dtstart = datetime_to_icaltime (self->date_start);
+
+  /* Translators:
+   * this is the format string for representing a date consisting of a month name
+   * and a date of month.
+   */
+  stm_date = icaltimetype_to_tm (dtstart);
+  e_utf8_strftime_fix_am_pm (start, 64, C_("event date format", "%B %d"), &stm_date);
+
+  if (self->date_end &&
+      (!all_day ||
+       (all_day && g_date_time_difference (self->date_end, self->date_start) / G_TIME_SPAN_DAY > 1)))
+    {
+      icaltimetype *dtend;
+
+      dtend = datetime_to_icaltime (self->date_end);
+
+      if (dtend->is_date)
+        dtend->day--;
+
+      /* Translators:
+       * this is the format string for representing a date consisting of a month name
+       * and a date of month.
+       */
+      etm_date = icaltimetype_to_tm (dtend);
+      e_utf8_strftime_fix_am_pm (end, 64, C_("event date format", "%B %d"), &etm_date);
+
+      title_date = g_strdup_printf (_("New Event from %s to %s"), start, end);
+
+      g_free (dtend);
+    }
+  else
+    {
+      title_date = g_strdup_printf (_("New Event on %s"), start);
+    }
+
+  gtk_label_set_label (GTK_LABEL (self->title_label), title_date);
+
+  g_free (title_date);
+  g_free (dtstart);
+}
+
+/*
+ * Callbacks
+ */
+
+static void
+on_source_added (GcalManager         *manager,
+                 ESource             *source,
+                 gboolean             enabled,
+                 GcalQuickAddPopover *self)
+{
+  ESource *default_source;
+  GtkWidget *row;
+
+  default_source = gcal_manager_get_default_source (manager);
+  row = create_row_for_source (manager, source, gcal_manager_is_client_writable (manager, source));
+
+  gtk_container_add (GTK_CONTAINER (self->calendars_listbox), row);
+
+  /* Select the default source whe first adding events */
+  if (source == default_source && !self->selected_row)
+    select_row (self, GTK_LIST_BOX_ROW (row));
+
+  g_clear_object (&default_source);
+}
+
+static void
+on_source_changed (GcalManager         *manager,
+                   ESource             *source,
+                   GcalQuickAddPopover *self)
+{
+  GtkWidget *row, *color_icon, *name_label;
+  GdkPixbuf *circle_pixbuf;
+  GdkRGBA color;
+
+  row = get_row_for_source (self, source);
+  color_icon = g_object_get_data (G_OBJECT (row), "color-icon");
+  name_label = g_object_get_data (G_OBJECT (row), "name-label");
+
+  /* If the source is writable now (or not), update sensitivity */
+  gtk_widget_set_sensitive (row, gcal_manager_is_client_writable (manager, source));
+
+  /* Setup the source color, in case it changed */
+  get_color_name_from_source (source, &color);
+  circle_pixbuf = get_circle_pixbuf_from_color (&color, 16);
+  gtk_image_set_from_pixbuf (GTK_IMAGE (color_icon), circle_pixbuf);
+
+  g_clear_object (&circle_pixbuf);
+
+  /* Also setup the row name, in case we just changed the source name */
+  gtk_label_set_text (GTK_LABEL (name_label), e_source_get_display_name (source));
+
+  gtk_list_box_invalidate_sort (GTK_LIST_BOX (self->calendars_listbox));
+
+  /*
+   * If the row is the selected one, make a fake selection so the
+   * icon & name of the source is updated at the events' page.
+   */
+  if (row == self->selected_row)
+    select_row (self, GTK_LIST_BOX_ROW (row));
+}
+
+static void
+on_source_removed (GcalManager         *manager,
+                   ESource             *source,
+                   GcalQuickAddPopover *self)
+{
+  GtkWidget *row;
+
+  row = get_row_for_source (self, source);
+
+  gtk_container_remove (GTK_CONTAINER (self->calendars_listbox), row);
+}
+
+/* Sort the calendars by their display name */
+static gint
+sort_func (GtkListBoxRow *row1,
+           GtkListBoxRow *row2,
+           gpointer       user_data)
+{
+  ESource *source1, *source2;
+  gchar *name1, *name2;
+  gint retval;
+
+  source1 = g_object_get_data (G_OBJECT (row1), "source");
+  source2 = g_object_get_data (G_OBJECT (row2), "source");
+
+  name1 = g_utf8_casefold (e_source_get_display_name (source1), -1);
+  name2 = g_utf8_casefold (e_source_get_display_name (source2), -1);
+
+  retval = g_strcmp0 (name1, name2);
+
+  g_free (name1);
+  g_free (name2);
+
+  return retval;
+}
+
+static void
+select_calendar_button_clicked (GcalQuickAddPopover *self)
+{
+  gtk_stack_set_visible_child_name (GTK_STACK (self->stack), "calendars");
+}
+
+static void
+back_button_clicked (GcalQuickAddPopover *self)
+{
+  gtk_stack_set_visible_child_name (GTK_STACK (self->stack), "events");
+}
+
+static void
+edit_or_create_event (GcalQuickAddPopover *self,
+                      GtkWidget           *button)
+{
+  ECalComponent *component;
+  GDateTime *date_start, *date_end;
+  GcalEvent *event;
+  ESource *source;
+  const gchar *summary;
+
+  if (!self->selected_row)
+    return;
+
+  source = g_object_get_data (G_OBJECT (self->selected_row), "source");
+
+  /* Gather start date */
+  date_start = g_date_time_new_local (g_date_time_get_year (self->date_start),
+                                      g_date_time_get_month (self->date_start),
+                                      g_date_time_get_day_of_month (self->date_start),
+                                      0, 0, 0);
+
+  /* Gather date end */
+  if (self->date_end)
+    {
+      date_end = g_date_time_new_local (g_date_time_get_year (self->date_end),
+                                        g_date_time_get_month (self->date_end),
+                                        g_date_time_get_day_of_month (self->date_end),
+                                        0, 0, 0);
+    }
+  else
+    {
+      date_end = g_date_time_add_days (date_start, 1);
+    }
+
+  /* Gather the summary */
+  if (gtk_entry_get_text_length (GTK_ENTRY (self->summary_entry)) > 0)
+    summary = gtk_entry_get_text (GTK_ENTRY (self->summary_entry));
+  else
+    summary = _("Unnamed event");
+
+  /* Create an ECalComponent from the data above */
+  component = build_component_from_details (summary, date_start, date_end);
+  event = gcal_event_new (source, component);
+
+  /* If we clicked edit button, send a signal; otherwise, create the event */
+  if (button == self->add_button)
+    gcal_manager_create_event (self->manager, event);
+  else
+    g_signal_emit (self, signals[EDIT_EVENT], 0, event);
+
+  gtk_widget_hide (GTK_WIDGET (self));
+
+  g_clear_pointer (&date_start, g_date_time_unref);
+  g_clear_pointer (&date_end, g_date_time_unref);
+  g_clear_object (&event);
+}
+
+static void
+summary_entry_text_changed (GtkEntry            *entry,
+                            GParamSpec          *pspec,
+                            GcalQuickAddPopover *self)
+{
+  gtk_widget_set_sensitive (self->add_button, gtk_entry_get_text_length (entry) > 0);
+}
+
+static void
+summary_entry_activated (GtkEntry            *entry,
+                         GcalQuickAddPopover *self)
+{
+  if (gtk_entry_get_text_length (entry) > 0)
+    edit_or_create_event (self, self->add_button);
+}
+
+
+/*
+ * Overrides
+ */
+
+static void
+gcal_quick_add_popover_finalize (GObject *object)
+{
+  GcalQuickAddPopover *self = (GcalQuickAddPopover *)object;
+
+  g_clear_object (&self->manager);
+
+  G_OBJECT_CLASS (gcal_quick_add_popover_parent_class)->finalize (object);
+}
+
+static void
+gcal_quick_add_popover_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  GcalQuickAddPopover *self = GCAL_QUICK_ADD_POPOVER (object);
+
+  switch (prop_id)
+    {
+    case PROP_DATE_END:
+      g_value_set_boxed (value, self->date_end);
+      break;
+
+    case PROP_DATE_START:
+      g_value_set_boxed (value, self->date_start);
+      break;
+
+    case PROP_MANAGER:
+      g_value_set_object (value, self->manager);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gcal_quick_add_popover_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+  GcalQuickAddPopover *self = GCAL_QUICK_ADD_POPOVER (object);
+
+  switch (prop_id)
+    {
+    case PROP_DATE_END:
+      gcal_quick_add_popover_set_date_end (self, g_value_get_boxed (value));
+      break;
+
+    case PROP_DATE_START:
+      gcal_quick_add_popover_set_date_start (self, g_value_get_boxed (value));
+      break;
+
+    case PROP_MANAGER:
+      gcal_quick_add_popover_set_manager (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gcal_quick_add_popover_closed (GtkPopover *popover)
+{
+  GcalQuickAddPopover *self;
+  GtkWidget *default_row;
+  ESource *default_source;
+
+  self = GCAL_QUICK_ADD_POPOVER (popover);
+  default_source = gcal_manager_get_default_source (self->manager);
+
+  /* Clear text */
+  gtk_entry_set_text (GTK_ENTRY (self->summary_entry), "");
+
+  /* Select the default row again */
+  default_row = get_row_for_source (self, default_source);
+  select_row (self, GTK_LIST_BOX_ROW (default_row));
+
+  g_clear_pointer (&self->date_start, g_date_time_unref);
+  g_clear_pointer (&self->date_end, g_date_time_unref);
+  g_clear_object (&default_source);
+}
+
+static void
+gcal_quick_add_popover_class_init (GcalQuickAddPopoverClass *klass)
+{
+  GtkPopoverClass *popover_class = GTK_POPOVER_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  popover_class->closed = gcal_quick_add_popover_closed;
+
+  object_class->finalize = gcal_quick_add_popover_finalize;
+  object_class->get_property = gcal_quick_add_popover_get_property;
+  object_class->set_property = gcal_quick_add_popover_set_property;
+
+  /**
+   * GcalQuickAddPopover::edit-event:
+   * @self: a #GcalQuickAddPopover
+   * @event: the new #GcalEvent
+   *
+   * Emited when the user clicks 'Edit event' button.
+   */
+  signals[EDIT_EVENT] = g_signal_new ("edit-event",
+                                      GCAL_TYPE_QUICK_ADD_POPOVER,
+                                      G_SIGNAL_RUN_LAST,
+                                      0, NULL, NULL, NULL,
+                                      G_TYPE_NONE,
+                                      1,
+                                      GCAL_TYPE_EVENT);
+
+  /**
+   * GcalQuickAddPopover::date-end:
+   *
+   * The end date of the new event.
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_DATE_END,
+                                   g_param_spec_boxed ("date-end",
+                                                       "End date of the event",
+                                                       "The end date of the new event",
+                                                       G_TYPE_DATE_TIME,
+                                                       G_PARAM_READWRITE));
+
+  /**
+   * GcalQuickAddPopover::date-start:
+   *
+   * The start date of the new event.
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_DATE_START,
+                                   g_param_spec_boxed ("date-start",
+                                                       "Start date of the event",
+                                                       "The start date of the new event",
+                                                       G_TYPE_DATE_TIME,
+                                                       G_PARAM_READWRITE));
+
+  /**
+   * GcalQuickAddPopover::manager:
+   *
+   * The manager of the application.
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_MANAGER,
+                                   g_param_spec_object ("manager",
+                                                        "Manager of the application",
+                                                        "The singleton manager of the application",
+                                                        GCAL_TYPE_MANAGER,
+                                                        G_PARAM_READWRITE));
+
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/quick-add-popover.ui");
+
+  gtk_widget_class_bind_template_child (widget_class, GcalQuickAddPopover, add_button);
+  gtk_widget_class_bind_template_child (widget_class, GcalQuickAddPopover, calendars_listbox);
+  gtk_widget_class_bind_template_child (widget_class, GcalQuickAddPopover, calendar_name_label);
+  gtk_widget_class_bind_template_child (widget_class, GcalQuickAddPopover, color_image);
+  gtk_widget_class_bind_template_child (widget_class, GcalQuickAddPopover, stack);
+  gtk_widget_class_bind_template_child (widget_class, GcalQuickAddPopover, summary_entry);
+  gtk_widget_class_bind_template_child (widget_class, GcalQuickAddPopover, title_label);
+
+  gtk_widget_class_bind_template_callback (widget_class, back_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, edit_or_create_event);
+  gtk_widget_class_bind_template_callback (widget_class, select_calendar_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, select_row);
+  gtk_widget_class_bind_template_callback (widget_class, summary_entry_activated);
+  gtk_widget_class_bind_template_callback (widget_class, summary_entry_text_changed);
+}
+
+static void
+gcal_quick_add_popover_init (GcalQuickAddPopover *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  gtk_list_box_set_sort_func (GTK_LIST_BOX (self->calendars_listbox), sort_func, NULL, NULL);
+}
+
+GtkWidget*
+gcal_quick_add_popover_new (void)
+{
+  return g_object_new (GCAL_TYPE_QUICK_ADD_POPOVER, NULL);
+}
+
+/**
+ * gcal_quick_add_popover_set_manager:
+ * @self: a #GcalQuickAddPopover
+ * @manager: a #GcalManager
+ *
+ * Sets the manager of the popover.
+ */
+void
+gcal_quick_add_popover_set_manager (GcalQuickAddPopover *self,
+                                    GcalManager         *manager)
+{
+  g_return_if_fail (GCAL_IS_QUICK_ADD_POPOVER (self));
+
+  if (g_set_object (&self->manager, manager))
+    {
+      GList *sources, *l;
+
+      /* Add currently leaded sources */
+      sources = gcal_manager_get_sources_connected (manager);
+
+      for (l = sources; l != NULL; l = g_list_next (l))
+        on_source_added (manager, l->data, gcal_manager_is_client_writable (manager, l->data), self);
+
+      g_list_free (sources);
+
+      /* Connect to the manager signals and keep the list updates */
+      g_signal_connect (manager, "source-added", G_CALLBACK (on_source_added), self);
+      g_signal_connect (manager, "source-changed", G_CALLBACK (on_source_changed), self);
+      g_signal_connect (manager, "source-removed", G_CALLBACK (on_source_removed), self);
+
+      g_object_notify (G_OBJECT (self), "manager");
+    }
+}
+
+/**
+ * gcal_quick_add_popover_get_date_start:
+ * @self: a #GcalQuickAddPopover
+ *
+ * Retrieves the start date of @self.
+ *
+ * Returns: (transfer none): a #GDateTime
+ */
+GDateTime*
+gcal_quick_add_popover_get_date_start (GcalQuickAddPopover *self)
+{
+  g_return_val_if_fail (GCAL_IS_QUICK_ADD_POPOVER (self), NULL);
+
+  return self->date_start;
+}
+
+/**
+ * gcal_quick_add_popover_set_date_start:
+ * @self: a #GcalQuickAddPopover
+ * @dt: a #GDateTime
+ *
+ * Sets the start date of the to-be-created event.
+ */
+void
+gcal_quick_add_popover_set_date_start (GcalQuickAddPopover *self,
+                                       GDateTime           *dt)
+{
+  g_return_if_fail (GCAL_IS_QUICK_ADD_POPOVER (self));
+
+  if (self->date_start != dt &&
+      (!self->date_start || !dt ||
+       (self->date_start && dt && !g_date_time_equal (self->date_start, dt))))
+    {
+      g_clear_pointer (&self->date_start, g_date_time_unref);
+      self->date_start = g_date_time_ref (dt);
+
+      update_header (self);
+
+      g_object_notify (G_OBJECT (self), "date-start");
+    }
+}
+
+/**
+ * gcal_quick_add_popover_get_date_end:
+ * @self: a #GcalQuickAddPopover
+ *
+ * Retrieves the end date of @self.
+ *
+ * Returns: (transfer none): a #GDateTime
+ */
+GDateTime*
+gcal_quick_add_popover_get_date_end (GcalQuickAddPopover *self)
+{
+  g_return_val_if_fail (GCAL_IS_QUICK_ADD_POPOVER (self), NULL);
+
+  return self->date_end;
+}
+
+/**
+ * gcal_quick_add_popover_set_date_end:
+ * @self: a #GcalQuickAddPopover
+ * @dt: a #GDateTime
+ *
+ * Sets the end date of the to-be-created event.
+ */
+void
+gcal_quick_add_popover_set_date_end (GcalQuickAddPopover *self,
+                                     GDateTime           *dt)
+{
+  g_return_if_fail (GCAL_IS_QUICK_ADD_POPOVER (self));
+
+  if (self->date_end != dt &&
+      (!self->date_end || !dt ||
+       (self->date_end && dt && !g_date_time_equal (self->date_end, dt))))
+    {
+      g_clear_pointer (&self->date_end, g_date_time_unref);
+      self->date_end = g_date_time_ref (dt);
+
+      update_header (self);
+
+      g_object_notify (G_OBJECT (self), "date-end");
+    }
+}
diff --git a/src/gcal-quick-add-popover.h b/src/gcal-quick-add-popover.h
new file mode 100644
index 0000000..b2b3180
--- /dev/null
+++ b/src/gcal-quick-add-popover.h
@@ -0,0 +1,51 @@
+/* gcal-quick-add-popover.h
+ *
+ * Copyright (C) 2016 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GCAL_QUICK_ADD_POPOVER_H
+#define GCAL_QUICK_ADD_POPOVER_H
+
+#include "gcal-event.h"
+#include "gcal-manager.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GCAL_TYPE_QUICK_ADD_POPOVER (gcal_quick_add_popover_get_type())
+
+G_DECLARE_FINAL_TYPE (GcalQuickAddPopover, gcal_quick_add_popover, GCAL, QUICK_ADD_POPOVER, GtkPopover)
+
+GtkWidget*           gcal_quick_add_popover_new                  (void);
+
+void                 gcal_quick_add_popover_set_manager          (GcalQuickAddPopover *self,
+                                                                  GcalManager         *manager);
+
+GDateTime*           gcal_quick_add_popover_get_date_start       (GcalQuickAddPopover *self);
+
+void                 gcal_quick_add_popover_set_date_start       (GcalQuickAddPopover *self,
+                                                                  GDateTime           *dt);
+
+GDateTime*           gcal_quick_add_popover_get_date_end         (GcalQuickAddPopover *self);
+
+void                 gcal_quick_add_popover_set_date_end         (GcalQuickAddPopover *self,
+                                                                  GDateTime           *dt);
+
+G_END_DECLS
+
+#endif /* GCAL_QUICK_ADD_POPOVER_H */



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