[california/wip/731543-attendees: 4/5] Display attendees in event viewer, editor
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [california/wip/731543-attendees: 4/5] Display attendees in event viewer, editor
- Date: Wed, 5 Nov 2014 03:20:58 +0000 (UTC)
commit 21359d9cc7afc0db2683973a2dc58aa1b87ed7a4
Author: Jim Nelson <jim yorba org>
Date: Tue Nov 4 18:28:54 2014 -0800
Display attendees in event viewer, editor
src/collection/collection-iterable.vala | 13 +++-
src/component/component-instance.vala | 93 +++++++++++++++++++++++++++++++
src/host/host-create-update-event.vala | 1 +
src/host/host-show-event.vala | 31 ++++++++++
src/rc/show-event.ui | 58 +++++++++++--------
5 files changed, 169 insertions(+), 27 deletions(-)
---
diff --git a/src/collection/collection-iterable.vala b/src/collection/collection-iterable.vala
index 3ee25ba..b5695bb 100644
--- a/src/collection/collection-iterable.vala
+++ b/src/collection/collection-iterable.vala
@@ -103,7 +103,7 @@ public class Iterable<G> : Object {
/**
* For { link to_string}.
*/
- public delegate string? ToString<G>(G element);
+ public delegate string? ToString<G>(G element, bool is_first, bool is_last);
/**
* For simple iteration of the { link Iterable}.
@@ -382,12 +382,19 @@ public class Iterable<G> : Object {
*
* If { link ToString} returns null or an empty string, nothing is appended to the final string.
*
+ * is_first is passed true to ToString if the string is the first element of the Iterable. If
+ * prior elements resulted in null being returned, then is_first will continue to be true. In
+ * other words, is_first is true if the built string so far is empty.
+ *
+ * is_last is only true when the last element of the Iterable has been reached.
+ *
* If the final string is empty, null is returned instead.
*/
public string? to_string(ToString<G> string_cb) {
StringBuilder builder = new StringBuilder();
- foreach (G element in this) {
- string? str = string_cb(element);
+ Gee.Iterator<G> iter = iterator();
+ while (iter.next()) {
+ string? str = string_cb(iter.get(), String.is_empty(builder.str), !iter.has_next());
if (!String.is_empty(str))
builder.append(str);
}
diff --git a/src/component/component-instance.vala b/src/component/component-instance.vala
index 89cc3c2..acdb8b3 100644
--- a/src/component/component-instance.vala
+++ b/src/component/component-instance.vala
@@ -39,6 +39,7 @@ public abstract class Instance : BaseObject, Gee.Hashable<Instance> {
public const string PROP_RDATES = "rdates";
public const string PROP_SEQUENCE = "sequence";
public const string PROP_MASTER = "master";
+ public const string PROP_ATTENDEES = "attendees";
protected const string PROP_IN_FULL_UPDATE = "in-full-update";
@@ -171,6 +172,20 @@ public abstract class Instance : BaseObject, Gee.Hashable<Instance> {
public int sequence { get; set; default = 0; }
/**
+ * ATTENDEEs for a VEVENT, VTODO, or VJOURNAL.
+ *
+ * This property returns a read-only view of the list of attendees. To add or remove attendees,
+ * use { link add_attendees}, { link remove_attendees}, and { link clear_attendees}.
+ * Because those methods always update the property itself and not merely modify the list,
+ * the property can be watched for changes with the "notify" and/or "altered" signals.
+ *
+ * No validity checking is performed on attendee mailto's.
+ *
+ * See [[https://tools.ietf.org/html/rfc5545#section-3.8.4.1]]
+ */
+ public Gee.List<string> attendees { get; private set; default = new Gee.ArrayList<string>(); }
+
+ /**
* The iCal component being represented by this { link Instance}.
*/
private iCal.icalcomponent _ical_component;
@@ -352,6 +367,17 @@ public abstract class Instance : BaseObject, Gee.Hashable<Instance> {
exdates = get_multiple_date_times(iCal.icalproperty_kind.EXDATE_PROPERTY);
rdates = get_multiple_date_times(iCal.icalproperty_kind.RDATE_PROPERTY);
+ // Currently only storing mailto's for attendees for now
+ unowned iCal.icalproperty? attendee_prop = ical_component.get_first_property(
+ iCal.icalproperty_kind.ATTENDEE_PROPERTY);
+ while (attendee_prop != null) {
+ unowned string mailto = attendee_prop.get_attendee();
+ if (!String.is_empty(mailto))
+ attendees.add(mailto);
+
+ attendee_prop = ical_component.get_next_property(iCal.icalproperty_kind.ATTENDEE_PROPERTY);
+ }
+
// save own copy of component; no ownership transferrance w/ current bindings
if (_ical_component != ical_component)
_ical_component = ical_component.clone();
@@ -398,6 +424,17 @@ public abstract class Instance : BaseObject, Gee.Hashable<Instance> {
set_multiple_date_times(iCal.icalproperty_kind.RDATE_PROPERTY, rdates);
break;
+ case PROP_ATTENDEES:
+ // TODO: Need to update iCal component with adjusted set of attendees, whether
+ // removed or appended
+ /*
+ unowned iCal.icalproperty? attendee_prop = ical_component.get_first_property(
+ iCal.icalproperty_kind.ATTENDEE_PROPERTY);
+ while (attendee_prop != null) {
+ }
+ */
+ break;
+
default:
altered = false;
break;
@@ -449,6 +486,62 @@ public abstract class Instance : BaseObject, Gee.Hashable<Instance> {
}
/**
+ * Add one or more mailto: URIs as { link attendees}.
+ *
+ * No URI-format checking is performed here; it is up to the caller to convert and deal with
+ * formatting issues. Also note that duplicates are allowed in the attendees list.
+ */
+ public void add_attendees(Gee.Collection<string> mailtos) {
+ Gee.List<string> copy = traverse<string>(attendees).to_array_list();
+ copy.add_all(mailtos);
+
+ attendees = copy;
+ }
+
+ /**
+ * Remove one or more mailto: URIs as (@link attendees}.
+ *
+ * See { link add_attendees} for notes about data validity and checking.
+ */
+ public void remove_attendees(Gee.Collection<string> mailtos) {
+ attendees = traverse<string>(attendees)
+ .filter(attendee => mailtos.contains(attendee))
+ .to_array_list();
+ }
+
+ /*
+ * Removes all { link attendees}.
+ */
+ public void clear_attendees() {
+ attendees = new Gee.ArrayList<string>();
+ }
+
+ /**
+ * Returns a single comma-delimited string for all attendees.
+ *
+ * Returns null if no attendees are associated with this { link Instance}.
+ */
+ public string? attendees_to_string() {
+ return traverse<string>(attendees).to_string(stringify_attendee);
+ }
+
+ private static string? stringify_attendee(string attendee, bool is_first, bool is_last) {
+ // Must parse correctly into URI
+ Soup.URI mailto;
+ try {
+ mailto = URI.parse(attendee);
+ } catch (Error err) {
+ return null;
+ }
+
+ // Must be a non-empty mailto:
+ if (mailto.scheme != "mailto" || String.is_empty(mailto.path))
+ return null;
+
+ return is_first ? mailto.path : ", %s".printf(mailto.path);
+ }
+
+ /**
* Returns an appropriate { link Component} instance for the iCalendar component.
*
* VCALENDARs should use { link Component.iCalendar}.
diff --git a/src/host/host-create-update-event.vala b/src/host/host-create-update-event.vala
index 922d8c6..1e678f8 100644
--- a/src/host/host-create-update-event.vala
+++ b/src/host/host-create-update-event.vala
@@ -170,6 +170,7 @@ public class CreateUpdateEvent : Gtk.Grid, Toolkit.Card {
location_entry.text = event.location ?? "";
description_textview.buffer.text = event.description ?? "";
+ attendees_entry.text = event.attendees_to_string() ?? "";
Component.Event master = event.is_master_instance ? event : (Component.Event) event.master;
diff --git a/src/host/host-show-event.vala b/src/host/host-show-event.vala
index 640c72d..439e1c7 100644
--- a/src/host/host-show-event.vala
+++ b/src/host/host-show-event.vala
@@ -17,6 +17,8 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
private const string FAMILY_NORMAL = "normal";
private const string FAMILY_REMOVING = "removing";
+ private const string DATA_TEXT = "california.text";
+
public string card_id { get { return ID; } }
public string? title { get { return null; } }
@@ -43,6 +45,12 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
private Gtk.Label where_text;
[GtkChild]
+ private Gtk.Label who_label;
+
+ [GtkChild]
+ private Gtk.Label who_text;
+
+ [GtkChild]
private Gtk.Label calendar_label;
[GtkChild]
@@ -119,6 +127,13 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
rotating_button_box.vexpand = true;
rotating_button_box.valign = Gtk.Align.END;
rotating_button_box_container.add(rotating_button_box);
+
+ // use this to setup tooltips for text labels that have been ellipsized ... this only works
+ // for GtkLabel's initialized with set_label(), so beware
+ summary_text.draw.connect(on_label_drawn);
+ when_text.draw.connect(on_label_drawn);
+ where_text.draw.connect(on_label_drawn);
+ who_text.draw.connect(on_label_drawn);
}
~ShowEvent() {
@@ -155,6 +170,9 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
set_label(when_label, when_text, event.get_event_time_pretty_string(Calendar.Date.PrettyFlag.NONE,
Calendar.ExactTimeSpan.PrettyFlag.NONE, Calendar.Timezone.local));
+ // attendees
+ set_label(who_label, who_text, event.attendees_to_string());
+
// calendar
set_label(calendar_label, calendar_text, event.calendar_source != null ? event.calendar_source.title
: null);
@@ -211,6 +229,19 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
label.no_show_all = true;
}
}
+
+ text.set_data(DATA_TEXT, str);
+ }
+
+ private bool on_label_drawn(Gtk.Widget widget, Cairo.Context ctx) {
+ // if ellipsized, add a tooltip, otherwise remove it
+ Gtk.Label label = (Gtk.Label) widget;
+ if (label.get_layout().is_ellipsized())
+ label.set_tooltip_text(label.get_data(DATA_TEXT));
+ else
+ label.set_tooltip_text("");
+
+ return false;
}
private void on_remove_button_clicked() {
diff --git a/src/rc/show-event.ui b/src/rc/show-event.ui
index f09531f..24b2063 100644
--- a/src/rc/show-event.ui
+++ b/src/rc/show-event.ui
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.16.1 -->
+<!-- Generated with glade 3.18.3 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<template class="CaliforniaHostShowEvent" parent="GtkGrid">
@@ -24,8 +24,6 @@
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
- <property name="width">1</property>
- <property name="height">1</property>
</packing>
</child>
<child>
@@ -67,8 +65,6 @@
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
- <property name="width">1</property>
- <property name="height">1</property>
</packing>
</child>
<child>
@@ -91,8 +87,6 @@
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
- <property name="width">1</property>
- <property name="height">1</property>
</packing>
</child>
<child>
@@ -109,8 +103,6 @@
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
- <property name="width">1</property>
- <property name="height">1</property>
</packing>
</child>
<child>
@@ -125,8 +117,6 @@
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
- <property name="width">1</property>
- <property name="height">1</property>
</packing>
</child>
<child>
@@ -141,8 +131,6 @@
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
- <property name="width">1</property>
- <property name="height">1</property>
</packing>
</child>
<child>
@@ -157,9 +145,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">2</property>
- <property name="width">1</property>
- <property name="height">1</property>
+ <property name="top_attach">3</property>
</packing>
</child>
<child>
@@ -173,17 +159,45 @@
</object>
<packing>
<property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="who_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="yalign">0</property>
+ <property name="label" translatable="yes" comments="Label for presenting the list of attendees
to an event.">Who</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="who_text">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label">(empty)</property>
+ <property name="selectable">True</property>
+ <property name="ellipsize">end</property>
+ <property name="single_line_mode">True</property>
+ <property name="max_width_chars">60</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
<property name="top_attach">2</property>
- <property name="width">1</property>
- <property name="height">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
- <property name="width">1</property>
- <property name="height">1</property>
</packing>
</child>
<child>
@@ -201,8 +215,6 @@
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
- <property name="width">1</property>
- <property name="height">1</property>
</packing>
</child>
<child>
@@ -219,8 +231,6 @@
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
- <property name="width">1</property>
- <property name="height">1</property>
</packing>
</child>
</template>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]