[gnome-calendar] event-widget: turn into a GtkBin subclass



commit a85337662925fa15b5b4ee4192ff4d27998341df
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Thu Sep 14 01:56:45 2017 -0300

    event-widget: turn into a GtkBin subclass
    
    The current GcalEventWidget implementation is a direct
    GtkWidget subclass, which means we have to take care of
    all the implementation details such as raising GdkWindows
    and managing the drawing all by ourselves.
    
    The event widget, however, can be easily described through
    a GtkBuilder file. This would simplify the implementation
    and makes us maintain less code.
    
    Fix that by making GcalEventWidget a GtkBin subclass.

 data/calendar.gresource.xml |    1 +
 data/theme/gtk-styles.css   |   18 +-
 data/ui/event-widget.ui     |   66 ++++
 src/css-code.h              |    7 +-
 src/gcal-event-widget.c     |  726 +++++++++++++++----------------------------
 src/gcal-event-widget.h     |    2 +-
 src/views/gcal-month-view.c |    6 +
 src/views/gcal-week-grid.c  |   10 +-
 8 files changed, 354 insertions(+), 482 deletions(-)
---
diff --git a/data/calendar.gresource.xml b/data/calendar.gresource.xml
index 6b5397e..4921985 100644
--- a/data/calendar.gresource.xml
+++ b/data/calendar.gresource.xml
@@ -6,6 +6,7 @@
     <file alias="date-chooser.ui" compressed="true" preprocess="xml-stripblanks">ui/date-chooser.ui</file>
     <file alias="date-selector.ui" compressed="true" preprocess="xml-stripblanks">ui/date-selector.ui</file>
     <file alias="edit-dialog.ui" compressed="true" preprocess="xml-stripblanks">ui/edit-dialog.ui</file>
+    <file alias="event-widget.ui" compressed="true" preprocess="xml-stripblanks">ui/event-widget.ui</file>
     <file alias="multi-choice.ui" compressed="true" preprocess="xml-stripblanks">ui/multi-choice.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>
diff --git a/data/theme/gtk-styles.css b/data/theme/gtk-styles.css
index 6f614c1..abaa206 100644
--- a/data/theme/gtk-styles.css
+++ b/data/theme/gtk-styles.css
@@ -212,28 +212,32 @@ popover.events button {
     box-shadow: none;
 }
 
-event-widget {
+
+/*
+ * Event widget
+ */
+
+event {
     border-radius: 2px;
-    margin: 2px 2px 1px;
-    padding: 3px 4px;
+    margin: 1px 1px 0 2px;
     font-size: 9pt;
 }
 
-event-widget.color-dark {
+event.color-dark {
     color: white;
     outline-color: rgba(0, 0, 0, 0.3);
 }
 
-event-widget.color-dark:backdrop {
+event.color-dark:backdrop {
     color: rgba(255, 255, 255, 0.3);
 }
 
-event-widget.color-light {
+event.color-light {
     color: black;
     outline-color: rgba(255, 255, 255, 0.5);
 }
 
-event-widget.color-light:backdrop {
+event.color-light:backdrop {
     color: rgba(0, 0, 0, 0.3);
 }
 
diff --git a/data/ui/event-widget.ui b/data/ui/event-widget.ui
new file mode 100644
index 0000000..68bc3ee
--- /dev/null
+++ b/data/ui/event-widget.ui
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GcalEventWidget" parent="GtkBin">
+    <child>
+      <object class="GtkEventBox" id="event_box">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="above_child">True</property>
+        <signal name="enter-notify-event" handler="enter_notify_event_cb" object="GcalEventWidget" 
swapped="no" />
+        <signal name="leave-notify-event" handler="leave_notify_event_cb" object="GcalEventWidget" 
swapped="no" />
+        <child>
+          <object class="GtkGrid" id="main_grid">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="column_spacing">6</property>
+            <property name="margin-top">1</property>
+            <property name="margin-bottom">1</property>
+            <property name="margin-start">4</property>
+            <property name="margin-end">4</property>
+            <property name="hexpand">False</property>
+            <child>
+              <object class="GtkLabel" id="hour_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0.0</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkImage"  id="alarm_icon">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="icon_name">alarm-symbolic</property>
+                <property name="pixel_size">12</property>
+                <property name="valign">center</property>
+              </object>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel"  id="summary_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="hexpand">True</property>
+                <property name="xalign">0</property>
+                <property name="ellipsize">end</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/css-code.h b/src/css-code.h
index a078614..e1e9061 100644
--- a/src/css-code.h
+++ b/src/css-code.h
@@ -257,7 +257,12 @@
 "                                    alpha(%2$s, 0) 10px,"\
 "                                    alpha(%2$s, 0) 16px,"\
 "                                    %2$s         17px);"\
-"}"
+"}"\
+".color-%1$d.slanted box { margin: 0 16px 0 16px; }"\
+".color-%1$d.slanted-start box { margin-left: 16px; }"\
+".color-%1$d.slanted-start:dir(rtl) box { margin-right: 16px; }"\
+".color-%1$d.slanted-end box { margin-right: 16px; }"\
+".color-%1$d.slanted-end:dir(rtl) box { margin-left: 16px; }"
 
 #endif /* CSS_CODE_H */
 
diff --git a/src/gcal-event-widget.c b/src/gcal-event-widget.c
index bee13f2..6025fc1 100644
--- a/src/gcal-event-widget.c
+++ b/src/gcal-event-widget.c
@@ -31,12 +31,19 @@
 
 struct _GcalEventWidget
 {
-  GtkWidget           parent;
+  GtkBin              parent;
 
   /* properties */
   GDateTime          *dt_start;
   GDateTime          *dt_end;
 
+  /* widgets */
+  GtkWidget          *alarm_icon;
+  GtkWidget          *event_box;
+  GtkWidget          *hour_label;
+  GtkWidget          *main_grid;
+  GtkWidget          *summary_label;
+
   /* internal data */
   gboolean            clock_format_24h : 1;
   gboolean            read_only : 1;
@@ -46,7 +53,6 @@ struct _GcalEventWidget
 
   GtkOrientation      orientation;
 
-  GdkWindow          *event_window;
   gboolean            button_pressed;
 };
 
@@ -66,98 +72,59 @@ enum
   NUM_SIGNALS
 };
 
+
+
+typedef enum
+{
+  CURSOR_NONE,
+  CURSOR_GRAB,
+  CURSOR_GRABBING
+} CursorType;
+
 static guint signals[NUM_SIGNALS] = { 0, };
 
-G_DEFINE_TYPE_WITH_CODE (GcalEventWidget, gcal_event_widget, GTK_TYPE_WIDGET,
+G_DEFINE_TYPE_WITH_CODE (GcalEventWidget, gcal_event_widget, GTK_TYPE_BIN,
                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL));
 
 /*
  * Auxiliary methods
  */
 
-static gchar*
-get_visible_text (GtkWidget         *widget,
-                  GtkOrientation     orientation)
+
+static void
+set_cursor (GtkWidget  *widget,
+            CursorType  type)
 {
-  GcalEventWidget *self;
-  gboolean is_ltr;
-  gchar *display_text;
-  gchar *escaped_text;
+  GdkDisplay *display;
+  GdkCursor *cursor;
 
-  self = GCAL_EVENT_WIDGET (widget);
-  is_ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
+  if (!gtk_widget_get_realized (widget))
+    return;
 
-  /*
-   * Since we set markup labels, we have to escape text before
-   * using it, or we get empty event widgets.
-   */
-  escaped_text = g_markup_escape_text (gcal_event_get_summary (self->event), -1);
+  display = gtk_widget_get_display (widget);
 
-  /*
-   * The text displayed in the widget includes the time when the event
-   * is timed, or not if the event is all day.
-   */
-  if (gcal_event_get_all_day (self->event))
-    {
-      display_text = escaped_text;
-    }
-  else
+  switch (type)
     {
-      GDateTime *local_start_time;
-      gchar *start_time;
-      local_start_time = g_date_time_to_local (gcal_event_get_date_start (self->event));
+    case CURSOR_NONE:
+      cursor = NULL;
+      break;
 
-      if (self->clock_format_24h)
-        start_time = g_date_time_format (local_start_time, "%R");
-      else
-        start_time = g_date_time_format (local_start_time, "%I:%M %P");
+    case CURSOR_GRAB:
+      cursor = gdk_cursor_new_from_name (display, "grab");
+      break;
 
-      /*
-       * When in horizontal, draw the elements sequentially; otherwise,
-       * draw the time in the first line, and the title in subsequent
-       * lines.
-       */
-      if (orientation == GTK_ORIENTATION_HORIZONTAL)
-        {
-          if (is_ltr)
-            display_text = g_strdup_printf ("(%s) %s", start_time, escaped_text);
-          else
-            display_text = g_strdup_printf ("%s (%s)", escaped_text, start_time);
-        }
-      else
-        {
-          display_text = g_strdup_printf ("<b>%s</b>\n%s", start_time, escaped_text);
-        }
+    case CURSOR_GRABBING:
+      cursor = gdk_cursor_new_from_name (display, "grabbing");
+      break;
 
-      g_clear_pointer (&local_start_time, g_date_time_unref);
-      g_clear_pointer (&escaped_text, g_free);
-      g_clear_pointer (&start_time, g_free);
+    default:
+      cursor = NULL;
     }
 
-    return display_text;
-}
-
-static gint
-get_vertical_text_height (GtkWidget *widget)
-{
-  GtkBorder border, padding;
-  GtkStyleContext *context;
-  PangoLayout *layout;
-  gint layout_height;
-  gchar* display_text;
-
-  context = gtk_widget_get_style_context (widget);
-  display_text = get_visible_text (widget, GTK_ORIENTATION_VERTICAL);
-  layout = gtk_widget_create_pango_layout (widget, display_text);
-  pango_layout_get_pixel_size (layout, NULL, &layout_height);
-  g_object_unref (layout);
-
-  gtk_style_context_get_border (context, gtk_style_context_get_state (context), &border);
-  gtk_style_context_get_padding (context, gtk_style_context_get_state (context), &padding);
-
-  g_clear_pointer(&display_text, g_free);
+  gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
+  gdk_display_flush (display);
 
-  return layout_height + padding.top + padding.bottom + border.top + border.bottom;
+  g_clear_object (&cursor);
 }
 
 static void
@@ -192,6 +159,10 @@ gcal_event_widget_update_style (GcalEventWidget *self)
     gtk_style_context_add_class (context, "slanted-start");
   else if (slanted_end)
     gtk_style_context_add_class (context, "slanted-end");
+
+  /* TODO: adjust margins based on the CSS gradients sizes, not hardcoded */
+  gtk_widget_set_margin_start (self->main_grid, slanted_start ? 20 : 4);
+  gtk_widget_set_margin_end (self->main_grid, slanted_end ? 20 : 4);
 }
 
 static void
@@ -397,10 +368,25 @@ gcal_event_widget_set_event_tooltip (GcalEventWidget *self,
   g_string_free (tooltip_mesg, TRUE);
 }
 
+static gchar*
+get_hour_label (GcalEventWidget *self)
+{
+  g_autoptr (GDateTime) local_start_time;
+
+  local_start_time = g_date_time_to_local (gcal_event_get_date_start (self->event));
+
+  if (self->clock_format_24h)
+    return g_date_time_format (local_start_time, "%R");
+  else
+    return g_date_time_format (local_start_time, "%I:%M %P");
+}
+
 static void
 gcal_event_widget_set_event_internal (GcalEventWidget *self,
                                       GcalEvent       *event)
 {
+  g_autofree gchar *hour_str = NULL;
+
   /*
    * This function is called only once, since the property is
    * set as CONSTRUCT_ONLY. Any other attempt to set an event
@@ -433,285 +419,63 @@ gcal_event_widget_set_event_internal (GcalEventWidget *self,
                             G_CALLBACK (gtk_widget_queue_draw),
                             self);
 
+  /* Tooltip */
   gcal_event_widget_set_event_tooltip (self, event);
-}
-
-
-/*
- * GObject overrides
- */
-
-static void
-gcal_event_widget_set_property (GObject      *object,
-                                guint         property_id,
-                                const GValue *value,
-                                GParamSpec   *pspec)
-{
-  GcalEventWidget *self = GCAL_EVENT_WIDGET (object);
-
-  switch (property_id)
-    {
-    case PROP_DATE_END:
-      gcal_event_widget_set_date_end (self, g_value_get_boxed (value));
-      break;
-
-    case PROP_DATE_START:
-      gcal_event_widget_set_date_start (self, g_value_get_boxed (value));
-      break;
-
-    case PROP_EVENT:
-      gcal_event_widget_set_event_internal (self, g_value_get_object (value));
-      break;
-
-    case PROP_ORIENTATION:
-      self->orientation = g_value_get_enum (value);
-      gtk_widget_queue_draw (GTK_WIDGET (object));
-      g_object_notify (object, "orientation");
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-    }
-}
-
-static void
-gcal_event_widget_get_property (GObject      *object,
-                                guint         property_id,
-                                GValue       *value,
-                                GParamSpec   *pspec)
-{
-  GcalEventWidget *self = GCAL_EVENT_WIDGET (object);
-
-  switch (property_id)
-    {
-    case PROP_DATE_END:
-      g_value_set_boxed (value, self->dt_end);
-      break;
 
-    case PROP_DATE_START:
-      g_value_set_boxed (value, self->dt_start);
-      break;
+  /* Alarm icon */
+  gtk_widget_set_visible (self->alarm_icon, gcal_event_has_alarms (event));
 
-    case PROP_EVENT:
-      g_value_set_object (value, self->event);
-      break;
+  /* Hour label */
+  hour_str = get_hour_label (self);
 
-    case PROP_ORIENTATION:
-      g_value_set_enum (value, self->orientation);
-      break;
+  gtk_widget_set_visible (self->hour_label, !gcal_event_get_all_day (event));
+  gtk_label_set_label (GTK_LABEL (self->hour_label), hour_str);
 
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-    }
+  /* Summary label */
+  g_object_bind_property (event,
+                          "summary",
+                          self->summary_label,
+                          "label",
+                          G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
 }
 
-static void
-gcal_event_widget_finalize (GObject *object)
-{
-  GcalEventWidget *self;
-
-  self = GCAL_EVENT_WIDGET (object);
-
-  /* disconnect signals */
-  g_signal_handlers_disconnect_by_func (self->event, update_color, self);
-  g_signal_handlers_disconnect_by_func (self->event, gtk_widget_queue_draw, self);
-
-  /* releasing properties */
-  g_clear_pointer (&self->css_class, g_free);
-  g_clear_object (&self->event);
 
-  G_OBJECT_CLASS (gcal_event_widget_parent_class)->finalize (object);
-}
-
-static void
-gcal_event_widget_get_preferred_width (GtkWidget *widget,
-                                       gint      *minimum,
-                                       gint      *natural)
-{
-  GtkBorder border, padding;
-  GtkStyleContext *context;
-  PangoLayout *layout;
-  gint layout_width;
-
-  context = gtk_widget_get_style_context (widget);
-  layout = gtk_widget_create_pango_layout (widget, "00:00:00 00:00");
-  pango_layout_get_pixel_size (layout, &layout_width, NULL);
-  g_object_unref (layout);
-
-  gtk_style_context_get_border (context, gtk_style_context_get_state (context), &border);
-  gtk_style_context_get_padding (context, gtk_style_context_get_state (context), &padding);
-
-  if (minimum != NULL)
-    *minimum = layout_width + padding.left + padding.right + border.left + border.right;
-  if (natural != NULL)
-    *natural = layout_width + padding.left + padding.right + border.left + border.right;
-}
-
-static void
-gcal_event_widget_get_preferred_height (GtkWidget *widget,
-                                        gint      *minimum,
-                                        gint      *natural)
-{
-  GcalEventWidget *self;
-  GtkBorder border, padding;
-  GtkStyleContext *context;
-  PangoLayout *layout;
-  gint layout_height;
-  gchar* display_text;
-
-  self = GCAL_EVENT_WIDGET (widget);
-  context = gtk_widget_get_style_context (widget);
-  display_text = get_visible_text (widget, self->orientation);
-  layout = gtk_widget_create_pango_layout (widget, display_text);
-  pango_layout_get_pixel_size (layout, NULL, &layout_height);
-  g_object_unref (layout);
-
-  gtk_style_context_get_border (context, gtk_style_context_get_state (context), &border);
-  gtk_style_context_get_padding (context, gtk_style_context_get_state (context), &padding);
-
-  if (minimum != NULL)
-    *minimum = layout_height + padding.top + padding.bottom + border.top + border.bottom;
-  if (natural != NULL)
-    *natural = layout_height + padding.top + padding.bottom + border.top + border.bottom;
-
-  g_clear_pointer(&display_text, g_free);
-}
-
-static void
-gcal_event_widget_realize (GtkWidget *widget)
-{
-  GcalEventWidget *self;
-  GdkWindow *parent_window;
-  GdkWindowAttr attributes;
-  gint attributes_mask;
-  GtkAllocation allocation;
-
-  GdkCursor* pointer_cursor;
-
-  self = GCAL_EVENT_WIDGET (widget);
-  gtk_widget_set_realized (widget, TRUE);
-
-  parent_window = gtk_widget_get_parent_window (widget);
-  gtk_widget_set_window (widget, parent_window);
-  g_object_ref (parent_window);
-
-  gtk_widget_get_allocation (widget, &allocation);
-
-  attributes.window_type = GDK_WINDOW_CHILD;
-  attributes.wclass = GDK_INPUT_ONLY;
-  attributes.x = allocation.x;
-  attributes.y = allocation.y;
-  attributes.width = allocation.width;
-  attributes.height = allocation.height;
-  attributes.event_mask = gtk_widget_get_events (widget);
-  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
-                            GDK_BUTTON_RELEASE_MASK |
-                            GDK_BUTTON1_MOTION_MASK |
-                            GDK_POINTER_MOTION_HINT_MASK |
-                            GDK_POINTER_MOTION_MASK |
-                            GDK_ENTER_NOTIFY_MASK |
-                            GDK_LEAVE_NOTIFY_MASK |
-                            GDK_SMOOTH_SCROLL_MASK |
-                            GDK_SCROLL_MASK);
-  attributes_mask = GDK_WA_X | GDK_WA_Y;
-
-  self->event_window = gdk_window_new (parent_window,
-                                       &attributes,
-                                       attributes_mask);
-  gtk_widget_register_window (widget, self->event_window);
-  gdk_window_show (self->event_window);
-
-  pointer_cursor = gdk_cursor_new_for_display (gdk_display_get_default (),
-                                               GDK_HAND1);
-  gdk_window_set_cursor (self->event_window, pointer_cursor);
-}
+/*
+ * Callbacks
+ */
 
-static void
-gcal_event_widget_unrealize (GtkWidget *widget)
+static gboolean
+enter_notify_event_cb (GtkWidget *event_box,
+                       GdkEvent  *event,
+                       GtkWidget *widget)
 {
-  GcalEventWidget *self;
-
-  self = GCAL_EVENT_WIDGET (widget);
-
-  if (self->event_window != NULL)
-    {
-      gtk_widget_unregister_window (widget, self->event_window);
-      gdk_window_destroy (self->event_window);
-      self->event_window = NULL;
-    }
-
-  GTK_WIDGET_CLASS (gcal_event_widget_parent_class)->unrealize (widget);
+  set_cursor (widget, CURSOR_GRAB);
+  return GDK_EVENT_PROPAGATE;
 }
 
-static void
-gcal_event_widget_map (GtkWidget *widget)
-{
-  GcalEventWidget *self;
-
-  self = GCAL_EVENT_WIDGET (widget);
-
-  GTK_WIDGET_CLASS (gcal_event_widget_parent_class)->map (widget);
-
-  if (self->event_window != NULL)
-    gdk_window_show (self->event_window);
-}
 
-static void
-gcal_event_widget_unmap (GtkWidget *widget)
+static gboolean
+leave_notify_event_cb (GtkWidget *event_box,
+                       GdkEvent  *event,
+                       GtkWidget *widget)
 {
-   GcalEventWidget *self;
-
-  self = GCAL_EVENT_WIDGET (widget);
-
-  GTK_WIDGET_CLASS (gcal_event_widget_parent_class)->unmap (widget);
-
-  if (self->event_window != NULL)
-    gdk_window_hide (self->event_window);
+  set_cursor (widget, CURSOR_NONE);
+  return GDK_EVENT_PROPAGATE;
 }
 
-static void
-gcal_event_widget_size_allocate (GtkWidget     *widget,
-                                 GtkAllocation *allocation)
-{
-  GcalEventWidget *self;
-
-  self = GCAL_EVENT_WIDGET (widget);
-  gtk_widget_set_allocation (widget, allocation);
 
-  if (gtk_widget_get_realized (widget))
-    {
-      gdk_window_move_resize (self->event_window,
-                              allocation->x,
-                              allocation->y,
-                              allocation->width,
-                              allocation->height);
-    }
-}
+/*
+ * GtkWidget overrides
+ */
 
 static gboolean
 gcal_event_widget_draw (GtkWidget *widget,
                         cairo_t   *cr)
 {
-  GcalEventWidget *self;
-
   GtkStyleContext *context;
-  GtkStateFlags state;
-  GtkBorder padding;
-
-  gint width, height, layout_height, x;
-  gint end_gap, icon_size = 0;
-  gboolean is_ltr;
+  guint width, height;
 
-  PangoLayout *layout;
-  PangoFontDescription *font_desc;
-  gchar *display_text;
-
-  self = GCAL_EVENT_WIDGET (widget);
   context = gtk_widget_get_style_context (widget);
-  state = gtk_style_context_get_state (context);
-  is_ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
-
-  gtk_style_context_get_padding (context, state, &padding);
 
   width = gtk_widget_get_allocated_width (widget);
   height = gtk_widget_get_allocated_height (widget);
@@ -719,131 +483,6 @@ gcal_event_widget_draw (GtkWidget *widget,
   gtk_render_background (context, cr, 0, 0, width, height);
   gtk_render_frame (context, cr, 0, 0, width, height);
 
-  /* Set the text to be displayed */
-  if (self->orientation == GTK_ORIENTATION_HORIZONTAL)
-    display_text = get_visible_text (widget, GTK_ORIENTATION_HORIZONTAL);
-  else
-    {
-      if (get_vertical_text_height (widget) >= height)
-        display_text = get_visible_text (widget, GTK_ORIENTATION_HORIZONTAL);
-      else
-        display_text = get_visible_text (widget, GTK_ORIENTATION_VERTICAL);
-    }
-
-  /* Retrieve the font description */
-  gtk_style_context_get (context, state, "font", &font_desc, NULL);
-  layout = gtk_widget_create_pango_layout (widget, "");
-  pango_layout_set_markup (layout, display_text, -1);
-  pango_layout_set_font_description (layout, font_desc);
-  pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
-
-  /* Icon size */
-  icon_size = ICON_SIZE;
-  end_gap = 0;
-
-  if (gcal_event_has_alarms (self->event))
-    end_gap += icon_size + (is_ltr ? padding.left : padding.right);
-
-  if (self->read_only)
-    end_gap += icon_size + (is_ltr ? padding.left : padding.right);
-
-  x = is_ltr ? padding.left : padding.left + end_gap;
-
-  pango_layout_set_width (layout, (width - (padding.left + padding.right + end_gap)) * PANGO_SCALE);
-  pango_layout_get_pixel_size (layout, NULL, &layout_height);
-  gtk_render_layout (context, cr, x, padding.top, layout);
-
-#if 0
-  /* TODO: find out a good way to handle this */
-  /*
-   * On vertical event widgets, we assume we might have space to draw the
-   * event's detail (or at least part of it).
-   */
-  if (self->orientation == GTK_ORIENTATION_VERTICAL &&
-      2 * padding.top + layout_height + padding.bottom < height &&
-      strlen (gcal_event_get_description (self->event)) > 0)
-    {
-      g_autofree gchar *description;
-
-      description = g_strdup_printf ("<small>%s</small>", gcal_event_get_description (self->event));
-      pango_layout_set_markup (layout, description, -1);
-      pango_layout_set_width (layout, (width - (padding.left + padding.right) ) * PANGO_SCALE);
-      pango_layout_set_height (layout, (height - (2 * padding.top + layout_height + padding.bottom) ) * 
PANGO_SCALE);
-      pango_layout_set_font_description (layout, font_desc);
-      pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
-
-      gtk_render_layout (context,
-                         cr,
-                         x,
-                         2 * padding.top + layout_height + padding.bottom,
-                         layout);
-    }
-#endif
-
-  x += is_ltr ? (width - end_gap - padding.left - padding.right) : (- padding.right - padding.left - 
icon_size);
-
-  /* render reminder icon */
-  if (gcal_event_has_alarms (self->event))
-    {
-      GtkIconTheme *icon_theme;
-      GtkIconInfo *icon_info;
-      GdkPixbuf *pixbuf;
-      gboolean was_symbolic;
-
-      icon_theme = gtk_icon_theme_get_default ();
-      icon_info = gtk_icon_theme_lookup_icon (icon_theme,
-                                              "alarm-symbolic",
-                                              icon_size,
-                                              0);
-      pixbuf = gtk_icon_info_load_symbolic_for_context (icon_info,
-                                                        context,
-                                                        &was_symbolic,
-                                                        NULL);
-
-      gtk_render_icon (context,
-                       cr,
-                       pixbuf,
-                       x + padding.left,
-                       padding.top);
-
-      x += is_ltr ? (padding.left + icon_size) : (- padding.right - icon_size);
-
-      g_object_unref (pixbuf);
-      cairo_paint (cr);
-    }
-
-  /* render locked icon */
-  if (self->read_only)
-    {
-      GtkIconTheme *icon_theme;
-      GtkIconInfo *icon_info;
-      GdkPixbuf *pixbuf;
-      gboolean was_symbolic;
-
-      icon_theme = gtk_icon_theme_get_default ();
-      icon_info = gtk_icon_theme_lookup_icon (icon_theme,
-                                              "changes-prevent-symbolic",
-                                              icon_size,
-                                              0);
-      pixbuf = gtk_icon_info_load_symbolic_for_context (icon_info,
-                                                        context,
-                                                        &was_symbolic,
-                                                        NULL);
-
-      gtk_render_icon (context,
-                       cr,
-                       pixbuf,
-                       x + padding.left,
-                       padding.top);
-
-      g_object_unref (pixbuf);
-      cairo_paint (cr);
-    }
-
-  pango_font_description_free (font_desc);
-  g_object_unref (layout);
-  g_free (display_text);
-
   return GTK_WIDGET_CLASS (gcal_event_widget_parent_class)->draw (widget, cr);
 }
 
@@ -855,7 +494,8 @@ gcal_event_widget_button_press_event (GtkWidget      *widget,
 
   self = GCAL_EVENT_WIDGET (widget);
   self->button_pressed = TRUE;
-  return TRUE;
+
+  return GDK_EVENT_STOP;
 }
 
 static gboolean
@@ -868,12 +508,62 @@ gcal_event_widget_button_release_event (GtkWidget      *widget,
 
   if (self->button_pressed)
     {
+      set_cursor (widget, CURSOR_NONE);
+
       self->button_pressed = FALSE;
       g_signal_emit (widget, signals[ACTIVATE], 0);
-      return TRUE;
+
+      return GDK_EVENT_STOP;
     }
 
-  return FALSE;
+  return GDK_EVENT_PROPAGATE;
+}
+
+static void
+gcal_event_widget_size_allocate (GtkWidget     *widget,
+                                 GtkAllocation *allocation)
+{
+  GcalEventWidget *self;
+
+  self = GCAL_EVENT_WIDGET (widget);
+
+  /* Try to put the summary label below if the orientation is vertical */
+  if (self->orientation == GTK_ORIENTATION_VERTICAL && !gcal_event_get_all_day (self->event))
+    {
+      gint minimum_grid_height;
+
+      gtk_label_set_line_wrap (GTK_LABEL (self->summary_label), TRUE);
+      /* This is needed to push the alarm icon to the end of the widget */
+      gtk_widget_set_hexpand (self->hour_label, TRUE);
+
+      gtk_container_child_set (GTK_CONTAINER (self->main_grid),
+                               self->summary_label,
+                               "left-attach", 0,
+                               "top-attach", 1,
+                               "width", 3,
+                               NULL);
+
+      gtk_widget_get_preferred_height (self->event_box, &minimum_grid_height, NULL);
+
+      /*
+       * There is no vertical space to put the summary label below the hour label.
+       * Thus, reset the label's original attributes.
+       */
+      if (allocation->height < minimum_grid_height)
+        {
+          gtk_container_child_set (GTK_CONTAINER (self->main_grid),
+                                   self->summary_label,
+                                   "left-attach", 1,
+                                   "top-attach", 0,
+                                   "width", 1,
+                                   NULL);
+
+          gtk_label_set_line_wrap (GTK_LABEL (self->summary_label), FALSE);
+          gtk_widget_set_hexpand (self->hour_label, FALSE);
+        }
+    }
+
+  GTK_WIDGET_CLASS (gcal_event_widget_parent_class)->size_allocate (widget, allocation);
 }
 
 static cairo_surface_t*
@@ -912,6 +602,7 @@ gcal_event_widget_drag_begin (GtkWidget      *widget,
   surface = get_dnd_icon (widget);
 
   gtk_drag_set_icon_surface (context, surface);
+  set_cursor (widget, CURSOR_GRABBING);
 
   g_clear_pointer (&surface, cairo_surface_destroy);
 }
@@ -923,8 +614,95 @@ gcal_event_widget_scroll_event (GtkWidget      *widget,
   return GDK_EVENT_PROPAGATE;
 }
 
+
+/*
+ * GObject overrides
+ */
+
+static void
+gcal_event_widget_set_property (GObject      *object,
+                                guint         property_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  GcalEventWidget *self = GCAL_EVENT_WIDGET (object);
+
+  switch (property_id)
+    {
+    case PROP_DATE_END:
+      gcal_event_widget_set_date_end (self, g_value_get_boxed (value));
+      break;
+
+    case PROP_DATE_START:
+      gcal_event_widget_set_date_start (self, g_value_get_boxed (value));
+      break;
+
+    case PROP_EVENT:
+      gcal_event_widget_set_event_internal (self, g_value_get_object (value));
+      break;
+
+    case PROP_ORIENTATION:
+      self->orientation = g_value_get_enum (value);
+      gtk_widget_queue_draw (GTK_WIDGET (object));
+      g_object_notify (object, "orientation");
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
 static void
-gcal_event_widget_class_init(GcalEventWidgetClass *klass)
+gcal_event_widget_get_property (GObject      *object,
+                                guint         property_id,
+                                GValue       *value,
+                                GParamSpec   *pspec)
+{
+  GcalEventWidget *self = GCAL_EVENT_WIDGET (object);
+
+  switch (property_id)
+    {
+    case PROP_DATE_END:
+      g_value_set_boxed (value, self->dt_end);
+      break;
+
+    case PROP_DATE_START:
+      g_value_set_boxed (value, self->dt_start);
+      break;
+
+    case PROP_EVENT:
+      g_value_set_object (value, self->event);
+      break;
+
+    case PROP_ORIENTATION:
+      g_value_set_enum (value, self->orientation);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+gcal_event_widget_finalize (GObject *object)
+{
+  GcalEventWidget *self;
+
+  self = GCAL_EVENT_WIDGET (object);
+
+  /* disconnect signals */
+  g_signal_handlers_disconnect_by_func (self->event, update_color, self);
+  g_signal_handlers_disconnect_by_func (self->event, gtk_widget_queue_draw, self);
+
+  /* releasing properties */
+  g_clear_pointer (&self->css_class, g_free);
+  g_clear_object (&self->event);
+
+  G_OBJECT_CLASS (gcal_event_widget_parent_class)->finalize (object);
+}
+
+static void
+gcal_event_widget_class_init (GcalEventWidgetClass *klass)
 {
   GObjectClass *object_class;
   GtkWidgetClass *widget_class;
@@ -935,17 +713,11 @@ gcal_event_widget_class_init(GcalEventWidgetClass *klass)
   object_class->finalize = gcal_event_widget_finalize;
 
   widget_class = GTK_WIDGET_CLASS (klass);
-  widget_class->get_preferred_width = gcal_event_widget_get_preferred_width;
-  widget_class->get_preferred_height = gcal_event_widget_get_preferred_height;
-  widget_class->realize = gcal_event_widget_realize;
-  widget_class->unrealize = gcal_event_widget_unrealize;
-  widget_class->map = gcal_event_widget_map;
-  widget_class->unmap = gcal_event_widget_unmap;
-  widget_class->size_allocate = gcal_event_widget_size_allocate;
-  widget_class->draw = gcal_event_widget_draw;
   widget_class->button_press_event = gcal_event_widget_button_press_event;
   widget_class->button_release_event = gcal_event_widget_button_release_event;
   widget_class->drag_begin = gcal_event_widget_drag_begin;
+  widget_class->draw = gcal_event_widget_draw;
+  widget_class->size_allocate = gcal_event_widget_size_allocate;
   widget_class->scroll_event = gcal_event_widget_scroll_event;
 
   /**
@@ -954,7 +726,7 @@ gcal_event_widget_class_init(GcalEventWidgetClass *klass)
    * The end date this widget represents. Notice that this may
    * differ from the event's end date. For example, if the event
    * spans more than one month and we're in Month View, the end
-   * date marks the last day this event is visible.
+   * date marks the last day this event widget is visible.
    */
   g_object_class_install_property (object_class,
                                    PROP_DATE_END,
@@ -970,7 +742,7 @@ gcal_event_widget_class_init(GcalEventWidgetClass *klass)
    * The start date this widget represents. Notice that this may
    * differ from the event's start date. For example, if the event
    * spans more than one month and we're in Month View, the start
-   * date marks the first day this event is visible.
+   * date marks the first day this event widget is visible.
    */
   g_object_class_install_property (object_class,
                                    PROP_DATE_START,
@@ -1009,7 +781,18 @@ gcal_event_widget_class_init(GcalEventWidgetClass *klass)
                                      G_TYPE_NONE,
                                      0);
 
-  gtk_widget_class_set_css_name (widget_class, "event-widget");
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/event-widget.ui");
+
+  gtk_widget_class_bind_template_child (widget_class, GcalEventWidget, alarm_icon);
+  gtk_widget_class_bind_template_child (widget_class, GcalEventWidget, event_box);
+  gtk_widget_class_bind_template_child (widget_class, GcalEventWidget, hour_label);
+  gtk_widget_class_bind_template_child (widget_class, GcalEventWidget, main_grid);
+  gtk_widget_class_bind_template_child (widget_class, GcalEventWidget, summary_label);
+
+  gtk_widget_class_bind_template_callback (widget_class, enter_notify_event_cb);
+  gtk_widget_class_bind_template_callback (widget_class, leave_notify_event_cb);
+
+  gtk_widget_class_set_css_name (widget_class, "event");
 }
 
 static void
@@ -1021,7 +804,8 @@ gcal_event_widget_init (GcalEventWidget *self)
   self->clock_format_24h = is_clock_format_24h ();
   self->orientation = GTK_ORIENTATION_HORIZONTAL;
 
-  gtk_widget_set_has_window (widget, FALSE);
+  gtk_widget_init_template (GTK_WIDGET (self));
+
   gtk_widget_set_can_focus (widget, TRUE);
 
   /* Setup the event widget as a drag source */
@@ -1048,7 +832,7 @@ gcal_event_widget_set_read_only (GcalEventWidget *event,
 {
   g_return_if_fail (GCAL_IS_EVENT_WIDGET (event));
 
-  if (read_only)
+  if (!read_only)
     {
       GtkWidget *widget = GTK_WIDGET (event);
 
diff --git a/src/gcal-event-widget.h b/src/gcal-event-widget.h
index b4034bd..f54216d 100644
--- a/src/gcal-event-widget.h
+++ b/src/gcal-event-widget.h
@@ -31,7 +31,7 @@ G_BEGIN_DECLS
 
 #define GCAL_TYPE_EVENT_WIDGET                    (gcal_event_widget_get_type ())
 
-G_DECLARE_FINAL_TYPE (GcalEventWidget, gcal_event_widget, GCAL, EVENT_WIDGET, GtkWidget)
+G_DECLARE_FINAL_TYPE (GcalEventWidget, gcal_event_widget, GCAL, EVENT_WIDGET, GtkBin)
 
 GtkWidget*           gcal_event_widget_new                       (GcalEvent          *event);
 
diff --git a/src/views/gcal-month-view.c b/src/views/gcal-month-view.c
index 7b80348..2bc25f6 100644
--- a/src/views/gcal-month-view.c
+++ b/src/views/gcal-month-view.c
@@ -1791,6 +1791,9 @@ gcal_month_view_size_allocate (GtkWidget     *widget,
               pos_x = cell_width * column + margin.left;
               pos_y = cell_height * (row + first_row_gap) + start_grid_y + margin.top;
 
+              /* TODO: find a better way to take the lines into account */
+              pos_y += 2;
+
               child_allocation.x = pos_x;
               child_allocation.y = pos_y + vertical_cell_space - size_left[cell_idx];
               child_allocation.width = cell_width * length - (margin.left + margin.right);
@@ -1865,6 +1868,9 @@ gcal_month_view_size_allocate (GtkWidget     *widget,
               pos_x = cell_width * (i % 7) + margin.left;
               pos_y = cell_height * ((i / 7) + first_row_gap) + start_grid_y + margin.top;
 
+              /* TODO: find a better way to take the lines into account */
+              pos_y += 2;
+
               child_allocation.x = pos_x;
               child_allocation.y = pos_y + vertical_cell_space - size_left[i];
               child_allocation.width = cell_width - (margin.left + margin.right);
diff --git a/src/views/gcal-week-grid.c b/src/views/gcal-week-grid.c
index a070a3d..a8eddea 100644
--- a/src/views/gcal-week-grid.c
+++ b/src/views/gcal-week-grid.c
@@ -615,8 +615,8 @@ gcal_week_grid_size_allocate (GtkWidget     *widget,
   gboolean ltr;
   gdouble minutes_height;
   gdouble column_width;
+  guint x, y;
   guint i;
-  guint x;
 
   /* No need to relayout stuff if nothing changed */
   if (allocation->height == gtk_widget_get_allocated_height (widget) &&
@@ -698,15 +698,21 @@ gcal_week_grid_size_allocate (GtkWidget     *widget,
           width = column_width / events_at_range - margin.left - margin.right;
           height = (data->end - data->start) * minutes_height - margin.top - margin.bottom;
           offset = (width + margin.left + margin.right) * widget_index;
+          y = (data->start % MINUTES_PER_DAY) * minutes_height + margin.top;
 
           if (ltr)
             x = column_width * i + offset + allocation->x + margin.left + 1;
           else
             x = allocation->width - width - (column_width * i + offset + allocation->x + margin.left + 1);
 
+          /* TODO: find a better way to handle line widths */
+          height -= 2;
+          width -= 1;
+          y += 1;
+
           /* Setup the child position and size */
           child_allocation.x = x;
-          child_allocation.y = (data->start % MINUTES_PER_DAY) * minutes_height + margin.top;
+          child_allocation.y = y;
           child_allocation.width = width;
           child_allocation.height = height;
 



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