[gnome-contacts/wip/nielsdg/plugins-for-form-fields] WIP: Rewrite the ContactEditor/ContactSheet
- From: Niels De Graef <nielsdg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-contacts/wip/nielsdg/plugins-for-form-fields] WIP: Rewrite the ContactEditor/ContactSheet
- Date: Sun, 9 Dec 2018 15:56:51 +0000 (UTC)
commit b2632f376f5214c9c80b89f8fbcb20b8aefed454
Author: Niels De Graef <nielsdegraef gmail com>
Date: Sat Dec 1 17:42:24 2018 +0100
WIP: Rewrite the ContactEditor/ContactSheet
data/ui/contacts-contact-form.ui | 14 +
src/contacts-contact-editor.vala | 846 ++++----------------------------
src/contacts-contact-form-field.vala | 916 +++++++++++++++++++++++++++++++++++
src/contacts-contact-form.vala | 85 +++-
src/contacts-contact-pane.vala | 52 +-
src/contacts-contact-sheet.vala | 227 ++-------
src/contacts-utils.vala | 9 -
src/meson.build | 1 +
8 files changed, 1162 insertions(+), 988 deletions(-)
---
diff --git a/data/ui/contacts-contact-form.ui b/data/ui/contacts-contact-form.ui
index b0c1fd1..b1b6943 100644
--- a/data/ui/contacts-contact-form.ui
+++ b/data/ui/contacts-contact-form.ui
@@ -3,6 +3,7 @@
<!-- interface-requires gtk+ 3.22 -->
<template class="ContactsContactForm" parent="GtkGrid">
<property name="visible">True</property>
+ <property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow" id="main_sw">
<property name="visible">True</property>
@@ -25,6 +26,19 @@
<property name="column_spacing">16</property>
<property name="margin">36</property>
<property name="margin_bottom">24</property>
+ <child>
+ <object class="GtkListBox" id="form_container">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <property name="selection_mode">none</property>
+ <property name="margin">6</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="left_attach">0</property>
+ <property name="width">4</property>
+ </packing>
+ </child>
</object>
</child>
</object>
diff --git a/src/contacts-contact-editor.vala b/src/contacts-contact-editor.vala
index c641877..edfddb8 100644
--- a/src/contacts-contact-editor.vala
+++ b/src/contacts-contact-editor.vala
@@ -73,10 +73,6 @@ public class Contacts.ContactEditor : ContactForm {
private weak Widget focus_widget;
- private Entry name_entry;
-
- private Avatar avatar;
-
[GtkChild]
private MenuButton add_detail_button;
@@ -86,23 +82,6 @@ public class Contacts.ContactEditor : ContactForm {
[GtkChild]
public Button remove_button;
- public struct PropertyData {
- Persona? persona;
- Value value;
- }
-
- struct RowData {
- AbstractFieldDetails details;
- }
-
- struct Field {
- bool changed;
- HashMap<int, RowData?> rows;
- }
-
- /* the key of the hash_map is the uid of the persona */
- private HashMap<string, HashMap<string, Field?>> writable_personas;
-
public bool has_birthday_row {
get; private set; default = false;
}
@@ -116,7 +95,6 @@ public class Contacts.ContactEditor : ContactForm {
}
construct {
- this.writable_personas = new HashMap<string, HashMap<string, Field?>> ();
this.container_grid.size_allocate.connect(on_container_grid_size_allocate);
}
@@ -146,679 +124,85 @@ public class Contacts.ContactEditor : ContactForm {
}
private void fill_in_contact () {
- int i = 3;
- int last_store_position = 0;
- bool is_first_persona = true;
-
var personas = this.contact.get_personas_for_display ();
foreach (var p in personas) {
- if (!is_first_persona) {
- this.container_grid.attach (create_persona_store_label (p), 0, i, 2);
- last_store_position = ++i;
- }
-
var rw_props = sort_persona_properties (p.writeable_properties);
if (rw_props.length != 0) {
- this.writable_personas[p.uid] = new HashMap<string, Field?> ();
- foreach (var prop in rw_props)
- add_edit_row (p, prop, ref i);
- }
-
- if (is_first_persona)
- this.last_row = i - 1;
-
- if (i != 3)
- is_first_persona = false;
-
- if (i == last_store_position) {
- i--;
- this.container_grid.get_child_at (0, i).destroy ();
+ foreach (var prop in rw_props) {
+ var field = add_edit_row (p, prop);
+ if (field != null)
+ add_field (field);
+ }
}
}
}
private void fill_in_empty () {
- this.last_row = 2;
-
- this.writable_personas["null-persona.hack"] = new HashMap<string, Field?> ();
foreach (var prop in DEFAULT_PROPS_NEW_CONTACT) {
var tok = prop.split (".");
- add_new_row_for_property (null, tok[0], tok[1].up ());
- }
-
- this.focus_widget = this.name_entry;
- }
-
- Value get_value_from_emails (HashMap<int, RowData?> rows) {
- var new_details = new HashSet<EmailFieldDetails>();
-
- foreach (var row_entry in rows.entries) {
- var combo = container_grid.get_child_at (0, row_entry.key) as TypeCombo;
- var entry = container_grid.get_child_at (1, row_entry.key) as Entry;
-
- /* Ignore empty entries. */
- if (entry.get_text () == "")
- continue;
-
- combo.update_details (row_entry.value.details);
- var details = new EmailFieldDetails (entry.get_text (), row_entry.value.details.parameters);
- new_details.add (details);
- }
- var new_value = Value (new_details.get_type ());
- new_value.set_object (new_details);
-
- return new_value;
- }
-
- Value get_value_from_phones (HashMap<int, RowData?> rows) {
- var new_details = new HashSet<PhoneFieldDetails>();
-
- foreach (var row_entry in rows.entries) {
- var combo = container_grid.get_child_at (0, row_entry.key) as TypeCombo;
- var entry = container_grid.get_child_at (1, row_entry.key) as Entry;
-
- /* Ignore empty entries. */
- if (entry.get_text () == "")
- continue;
-
- combo.update_details (row_entry.value.details);
- var details = new PhoneFieldDetails (entry.get_text (), row_entry.value.details.parameters);
- new_details.add (details);
- }
- var new_value = Value (new_details.get_type ());
- new_value.set_object (new_details);
- return new_value;
- }
-
- Value get_value_from_urls (HashMap<int, RowData?> rows) {
- var new_details = new HashSet<UrlFieldDetails>();
-
- foreach (var row_entry in rows.entries) {
- var entry = container_grid.get_child_at (1, row_entry.key) as Entry;
-
- /* Ignore empty entries. */
- if (entry.get_text () == "")
- continue;
-
- var details = new UrlFieldDetails (entry.get_text (), row_entry.value.details.parameters);
- new_details.add (details);
- }
- var new_value = Value (new_details.get_type ());
- new_value.set_object (new_details);
- return new_value;
- }
-
- Value get_value_from_nickname (HashMap<int, RowData?> rows) {
- var new_value = Value (typeof (string));
- foreach (var row_entry in rows.entries) {
- var entry = container_grid.get_child_at (1, row_entry.key) as Entry;
-
- /* Ignore empty entries. */
- if (entry.get_text () == "")
- continue;
-
- new_value.set_string (entry.get_text ());
- }
- return new_value;
- }
-
- Value get_value_from_birthday (HashMap<int, RowData?> rows) {
- var new_value = Value (typeof (DateTime));
- foreach (var row_entry in rows.entries) {
- var box = container_grid.get_child_at (1, row_entry.key) as Grid;
- var day_spin = box.get_child_at (0, 0) as SpinButton;
- var combo = box.get_child_at (1, 0) as ComboBoxText;
- var year_spin = box.get_child_at (2, 0) as SpinButton;
-
- var bday = new DateTime.local (year_spin.get_value_as_int (),
- combo.get_active () + 1,
- day_spin.get_value_as_int (),
- 0, 0, 0);
- bday = bday.to_utc ();
-
- new_value.set_boxed (bday);
- }
- return new_value;
- }
-
- Value get_value_from_notes (HashMap<int, RowData?> rows) {
- var new_details = new HashSet<NoteFieldDetails>();
-
- foreach (var row_entry in rows.entries) {
- var text = (container_grid.get_child_at (1, row_entry.key) as Bin).get_child () as TextView;
- TextIter start, end;
- text.get_buffer ().get_start_iter (out start);
- text.get_buffer ().get_end_iter (out end);
- var value = text.get_buffer ().get_text (start, end, true);
- if (value != "") {
- var details = new NoteFieldDetails (value, row_entry.value.details.parameters);
- new_details.add (details);
- }
- }
- var new_value = Value (new_details.get_type ());
- new_value.set_object (new_details);
- return new_value;
- }
-
- Value get_value_from_addresses (HashMap<int, RowData?> rows) {
- var new_details = new HashSet<PostalAddressFieldDetails>();
-
- foreach (var row_entry in rows.entries) {
- var combo = container_grid.get_child_at (0, row_entry.key) as TypeCombo;
- var addr_editor = container_grid.get_child_at (1, row_entry.key) as AddressEditor;
- combo.update_details (row_entry.value.details);
-
- var new_value = new PostalAddress (addr_editor.details.value.po_box,
- addr_editor.details.value.extension,
- addr_editor.details.value.street,
- addr_editor.details.value.locality,
- addr_editor.details.value.region,
- addr_editor.details.value.postal_code,
- addr_editor.details.value.country,
- addr_editor.details.value.address_format,
- addr_editor.details.id);
- for (int i = 0; i < addr_editor.entries.length; i++)
- new_value.set (AddressEditor.postal_element_props[i], addr_editor.entries[i].get_text ());
-
- var details = new PostalAddressFieldDetails(new_value, row_entry.value.details.parameters);
- new_details.add (details);
- }
- var new_value = Value (new_details.get_type ());
- new_value.set_object (new_details);
- return new_value;
- }
-
- void set_field_changed (int row) {
- foreach (var fields in writable_personas.values) {
- foreach (var entry in fields.entries) {
- if (row in entry.value.rows.keys) {
- if (entry.value.changed)
- return;
-
- entry.value.changed = true;
- return;
- }
- }
- }
- }
-
- new void remove_row (int row) {
- foreach (var fields in writable_personas.values) {
- foreach (var field_entry in fields.entries) {
- foreach (var idx in field_entry.value.rows.keys) {
- if (idx == row) {
- var child = container_grid.get_child_at (0, row);
- child.destroy ();
- child = container_grid.get_child_at (1, row);
- child.destroy ();
- child = container_grid.get_child_at (3, row);
- child.destroy ();
-
- field_entry.value.changed = true;
- field_entry.value.rows.unset (row);
- return;
- }
- }
- }
- }
- }
-
- void attach_row_with_entry (int row, TypeSet type_set, AbstractFieldDetails details, string value, string?
type = null) {
- var combo = new TypeCombo (type_set);
- combo.set_hexpand (false);
- combo.set_active (details);
- if (type != null)
- combo.set_to (type);
- combo.set_valign (Align.CENTER);
- container_grid.attach (combo, 0, row, 1, 1);
-
- var value_entry = new Entry ();
- value_entry.set_text (value);
- value_entry.set_hexpand (true);
- container_grid.attach (value_entry, 1, row, 1, 1);
-
- if (type_set == TypeSet.email) {
- value_entry.placeholder_text = _("Add email");
- } else if (type_set == TypeSet.phone) {
- value_entry.placeholder_text = _("Add number");
- }
-
- var delete_button = new Button.from_icon_name ("user-trash-symbolic", IconSize.MENU);
- delete_button.get_accessible ().set_name (_("Delete field"));
- container_grid.attach (delete_button, 3, row, 1, 1);
-
- /* Notify change to upper layer */
- combo.changed.connect (() => {
- set_field_changed (get_current_row (combo));
- });
- value_entry.changed.connect (() => {
- set_field_changed (get_current_row (value_entry));
- });
- delete_button.clicked.connect (() => {
- remove_row (get_current_row (delete_button));
- });
+ var field = add_edit_row (null, tok[0], true, tok[1].up ());
+ if (field != null)
+ add_field (field);
- if (value == "")
- focus_widget = value_entry;
- }
-
- void attach_row_with_entry_labeled (string title, AbstractFieldDetails? details, string value, int row) {
- var title_label = new Label (title);
- title_label.set_hexpand (false);
- title_label.set_halign (Align.START);
- title_label.margin_end = 6;
- container_grid.attach (title_label, 0, row, 1, 1);
-
- var value_entry = new Entry ();
- value_entry.set_text (value);
- value_entry.set_hexpand (true);
- container_grid.attach (value_entry, 1, row, 1, 1);
-
- var delete_button = new Button.from_icon_name ("user-trash-symbolic", IconSize.MENU);
- delete_button.get_accessible ().set_name (_("Delete field"));
- container_grid.attach (delete_button, 3, row, 1, 1);
-
- /* Notify change to upper layer */
- value_entry.changed.connect (() => {
- set_field_changed (get_current_row (value_entry));
- });
- delete_button.clicked.connect_after (() => {
- remove_row (get_current_row (delete_button));
- });
-
- if (value == "")
- focus_widget = value_entry;
- }
-
- void attach_row_with_text_labeled (string title, AbstractFieldDetails? details, string value, int row) {
- var title_label = new Label (title);
- title_label.set_hexpand (false);
- title_label.set_halign (Align.START);
- title_label.set_valign (Align.START);
- title_label.margin_top = 3;
- title_label.margin_end = 6;
- container_grid.attach (title_label, 0, row, 1, 1);
-
- var sw = new ScrolledWindow (null, null);
- sw.set_shadow_type (ShadowType.OUT);
- sw.set_size_request (-1, 100);
- var value_text = new TextView ();
- value_text.get_buffer ().set_text (value);
- value_text.set_hexpand (true);
- sw.add (value_text);
- container_grid.attach (sw, 1, row, 1, 1);
-
- var delete_button = new Button.from_icon_name ("user-trash-symbolic", IconSize.MENU);
- delete_button.get_accessible ().set_name (_("Delete field"));
- delete_button.set_valign (Align.START);
- container_grid.attach (delete_button, 3, row, 1, 1);
-
- /* Notify change to upper layer */
- value_text.get_buffer ().changed.connect (() => {
- set_field_changed (get_current_row (sw));
- });
- delete_button.clicked.connect (() => {
- remove_row (get_current_row (delete_button));
- /* eventually will need to check against the details type */
- has_notes_row = false;
- });
-
- if (value == "")
- focus_widget = value_text;
- }
-
- delegate void AdjustingDateFn();
-
- void attach_row_for_birthday (string title, AbstractFieldDetails? details, DateTime birthday, int row) {
- var title_label = new Label (title);
- title_label.set_hexpand (false);
- title_label.set_halign (Align.START);
- title_label.margin_end = 6;
- container_grid.attach (title_label, 0, row, 1, 1);
-
- var box = new Grid ();
- box.set_column_spacing (12);
- var day_spin = new SpinButton.with_range (1.0, 31.0, 1.0);
- day_spin.set_digits (0);
- day_spin.numeric = true;
- day_spin.set_value ((double)birthday.to_local ().get_day_of_month ());
-
- var month_combo = new ComboBoxText ();
- var january = new DateTime.local (1, 1, 1, 1, 1, 1);
- for (int i = 0; i < 12; i++) {
- var month = january.add_months (i);
- month_combo.append_text (month.format ("%B"));
+ container_grid.show_all ();
}
- month_combo.set_active (birthday.to_local ().get_month () - 1);
- month_combo.hexpand = true;
-
- var year_spin = new SpinButton.with_range (1800, 3000, 1);
- year_spin.set_digits (0);
- year_spin.numeric = true;
- year_spin.set_value ((double)birthday.to_local ().get_year ());
-
- box.add (day_spin);
- box.add (month_combo);
- box.add (year_spin);
-
- container_grid.attach (box, 1, row, 1, 1);
-
- var delete_button = new Button.from_icon_name ("user-trash-symbolic", IconSize.MENU);
- delete_button.get_accessible ().set_name (_("Delete field"));
- container_grid.attach (delete_button, 3, row, 1, 1);
-
- AdjustingDateFn fn = () => {
- int[] month_of_31 = {3, 5, 8, 10};
- if (month_combo.get_active () in month_of_31) {
- day_spin.set_range (1, 30);
- } else if (month_combo.get_active () == 1) {
- if (year_spin.get_value_as_int () % 4 == 0 &&
- year_spin.get_value_as_int () % 100 != 0) {
- day_spin.set_range (1, 29);
- } else {
- day_spin.set_range (1, 28);
- }
- }
- };
-
- /* Notify change to upper layer */
- day_spin.changed.connect (() => {
- set_field_changed (get_current_row (day_spin));
- });
- month_combo.changed.connect (() => {
- set_field_changed (get_current_row (month_combo));
-
- /* adjusting day_spin value using selected month constraints*/
- fn ();
- });
- year_spin.changed.connect (() => {
- set_field_changed (get_current_row (year_spin));
- fn ();
- });
- delete_button.clicked.connect (() => {
- remove_row (get_current_row (delete_button));
- has_birthday_row = false;
- });
+ this.focus_widget = this.name_widget;
}
- void attach_row_for_address (int row, TypeSet type_set, PostalAddressFieldDetails details, string? type =
null) {
- var combo = new TypeCombo (type_set);
- combo.set_hexpand (false);
- combo.set_active (details);
- if (type != null)
- combo.set_to (type);
- container_grid.attach (combo, 0, row, 1, 1);
-
- var value_address = new AddressEditor (details);
- container_grid.attach (value_address, 1, row, 1, 1);
-
- var delete_button = new Button.from_icon_name ("user-trash-symbolic", IconSize.MENU);
- delete_button.get_accessible ().set_name (_("Delete field"));
- delete_button.set_valign (Align.START);
- container_grid.attach (delete_button, 3, row, 1, 1);
-
- /* Notify change to upper layer */
- combo.changed.connect (() => {
- set_field_changed (get_current_row (combo));
- });
- value_address.changed.connect (() => {
- set_field_changed (get_current_row (value_address));
- });
- delete_button.clicked.connect (() => {
- remove_row (get_current_row (delete_button));
- });
-
- focus_widget = value_address;
- }
-
- void add_edit_row (Persona? p, string prop_name, ref int row, bool add_empty = false, string? type = null)
{
- /* Here, we will need to add manually every type of field,
- * we're planning to allow editing on */
- string persona_uid = p != null ? p.uid : "null-persona.hack";
+ PropertyField? add_edit_row (Persona? p, string prop_name, bool add_empty = false, string? type = null) {
switch (prop_name) {
case "email-addresses":
- var rows = new HashMap<int, RowData?> ();
- if (add_empty) {
- var detail_field = new EmailFieldDetails ("");
- attach_row_with_entry (row, TypeSet.email, detail_field, "", type);
- rows.set (row, { detail_field });
- row++;
- } else {
- var details = p as EmailDetails;
- if (details != null) {
- var emails = Contact.sort_fields<EmailFieldDetails>(details.email_addresses);
- foreach (var email in emails) {
- attach_row_with_entry (row, TypeSet.email, email, email.value);
- rows.set (row, { email });
- row++;
- }
- }
- }
- if (! rows.is_empty) {
- if (writable_personas[persona_uid].has_key (prop_name)) {
- foreach (var entry in rows.entries) {
- writable_personas[persona_uid][prop_name].rows.set (entry.key, entry.value);
- }
- } else {
- writable_personas[persona_uid].set (prop_name, { false, rows });
- }
- }
+ if (add_empty)
+ return new EditableEmailsField.empty ();
+ if (EmailsField.should_show (p))
+ return new EditableEmailsField (p);
break;
+
case "phone-numbers":
- var rows = new HashMap<int, RowData?> ();
- if (add_empty) {
- var detail_field = new PhoneFieldDetails ("");
- attach_row_with_entry (row, TypeSet.phone, detail_field, "", type);
- rows.set (row, { detail_field });
- row++;
- } else {
- var details = p as PhoneDetails;
- if (details != null) {
- var phones = Contact.sort_fields<PhoneFieldDetails>(details.phone_numbers);
- foreach (var phone in phones) {
- attach_row_with_entry (row, TypeSet.phone, phone, phone.value, type);
- rows.set (row, { phone });
- row++;
- }
- }
- }
- if (! rows.is_empty) {
- if (writable_personas[persona_uid].has_key (prop_name)) {
- foreach (var entry in rows.entries) {
- writable_personas[persona_uid][prop_name].rows.set (entry.key, entry.value);
- }
- } else {
- writable_personas[persona_uid].set (prop_name, { false, rows });
- }
- }
+ if (add_empty)
+ return new EditablePhoneNrsField.empty ();
+ if (PhoneNrsField.should_show (p))
+ return new EditablePhoneNrsField (p);
break;
+
case "urls":
- var rows = new HashMap<int, RowData?> ();
- if (add_empty) {
- var detail_field = new UrlFieldDetails ("");
- attach_row_with_entry_labeled (_("Website"), detail_field, "", row);
- rows.set (row, { detail_field });
- row++;
- } else {
- var url_details = p as UrlDetails;
- if (url_details != null) {
- foreach (var url in url_details.urls) {
- attach_row_with_entry_labeled (_("Website"), url, url.value, row);
- rows.set (row, { url });
- row++;
- }
- }
- }
- if (! rows.is_empty) {
- if (writable_personas[persona_uid].has_key (prop_name)) {
- foreach (var entry in rows.entries) {
- writable_personas[persona_uid][prop_name].rows.set (entry.key, entry.value);
- }
- } else {
- writable_personas[persona_uid].set (prop_name, { false, rows });
- }
- }
+ if (add_empty)
+ return new EditableUrlsField.empty ();
+ if (UrlsField.should_show (p))
+ return new EditableUrlsField (p);
break;
+
case "nickname":
- var rows = new HashMap<int, RowData?> ();
- if (add_empty) {
- attach_row_with_entry_labeled (_("Nickname"), null, "", row);
- rows.set (row, { null });
- row++;
- } else {
- var name_details = p as NameDetails;
- if (name_details != null) {
- if (is_set (name_details.nickname)) {
- attach_row_with_entry_labeled (_("Nickname"), null, name_details.nickname, row);
- rows.set (row, { null });
- row++;
- }
- }
- }
- if (! rows.is_empty) {
- has_nickname_row = true;
- var delete_button = container_grid.get_child_at (3, row - 1) as Button;
- delete_button.clicked.connect (() => {
- has_nickname_row = false;
- });
-
- if (writable_personas[persona_uid].has_key (prop_name)) {
- foreach (var entry in rows.entries) {
- writable_personas[persona_uid][prop_name].rows.set (entry.key, entry.value);
- }
- } else {
- writable_personas[persona_uid].set (prop_name, { false, rows });
- }
- }
+ if (add_empty)
+ return new EditableNicknameField.empty ();
+ if (NicknameField.should_show (p))
+ return new EditableNicknameField (p);
break;
+
case "birthday":
- var rows = new HashMap<int, RowData?> ();
- if (add_empty) {
- var today = new DateTime.now_local ();
- attach_row_for_birthday (_("Birthday"), null, today, row);
- rows.set (row, { null });
- row++;
- } else {
- var birthday_details = p as BirthdayDetails;
- if (birthday_details != null) {
- if (birthday_details.birthday != null) {
- attach_row_for_birthday (_("Birthday"), null, birthday_details.birthday, row);
- rows.set (row, { null });
- row++;
- }
- }
- }
- if (! rows.is_empty) {
- has_birthday_row = true;
- writable_personas[persona_uid].set (prop_name, { add_empty, rows });
- }
+ if (add_empty)
+ return new EditableBirthdayField.empty ();
+ if (BirthdayField.should_show (p))
+ return new EditableBirthdayField (p);
break;
+
case "notes":
- var rows = new HashMap<int, RowData?> ();
- if (add_empty) {
- var detail_field = new NoteFieldDetails ("");
- attach_row_with_text_labeled (_("Note"), detail_field, "", row);
- rows.set (row, { detail_field });
- row++;
- } else {
- var note_details = p as NoteDetails;
- if (note_details != null || add_empty) {
- foreach (var note in note_details.notes) {
- attach_row_with_text_labeled (_("Note"), note, note.value, row);
- rows.set (row, { note });
- row++;
- }
- }
- }
- if (! rows.is_empty) {
- has_notes_row = true;
- if (writable_personas[persona_uid].has_key (prop_name)) {
- foreach (var entry in rows.entries) {
- writable_personas[persona_uid][prop_name].rows.set (entry.key, entry.value);
- }
- } else {
- writable_personas[persona_uid].set (prop_name, { false, rows });
- }
- }
+ if (add_empty)
+ return new EditableNotesField.empty ();
+ if (NotesField.should_show (p))
+ return new EditableNotesField (p);
break;
+
case "postal-addresses":
- var rows = new HashMap<int, RowData?> ();
- if (add_empty) {
- var detail_field = new PostalAddressFieldDetails (
- new PostalAddress (null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null));
- attach_row_for_address (row, TypeSet.general, detail_field, type);
- rows.set (row, { detail_field });
- row++;
- } else {
- var address_details = p as PostalAddressDetails;
- if (address_details != null) {
- foreach (var addr in address_details.postal_addresses) {
- attach_row_for_address (row, TypeSet.general, addr, type);
- rows.set (row, { addr });
- row++;
- }
- }
- }
- if (! rows.is_empty) {
- if (writable_personas[persona_uid].has_key (prop_name)) {
- foreach (var entry in rows.entries) {
- writable_personas[persona_uid][prop_name].rows.set (entry.key, entry.value);
- }
- } else {
- writable_personas[persona_uid].set (prop_name, { false, rows });
- }
- }
+ if (add_empty)
+ return new EditablePostalAddressesField.empty ();
+ if (PostalAddressesField.should_show (p))
+ return new EditablePostalAddressesField (p);
break;
}
- }
-
- int get_current_row (Widget child) {
- int row;
-
- container_grid.child_get (child, "top-attach", out row);
- return row;
- }
- void insert_row_at (int idx) {
- foreach (var field_maps in writable_personas.values) {
- foreach (var field in field_maps.values) {
- foreach (var row in field.rows.keys) {
- if (row >= idx) {
- var new_rows = new HashMap <int, RowData?> ();
- foreach (var old_row in field.rows.keys) {
- /* move all rows +1 */
- new_rows.set (old_row + 1, field.rows[old_row]);
- }
- field.rows = new_rows;
- break;
- }
- }
- }
- }
- foreach (var entry in writable_personas.entries) {
- foreach (var field_entry in entry.value.entries) {
- foreach (var row in field_entry.value.rows.keys) {
- if (row >= idx) {
- var new_rows = new HashMap <int, RowData?> ();
- foreach (var old_row in field_entry.value.rows.keys) {
- new_rows.set (old_row + 1, field_entry.value.rows[old_row]);
- }
- field_entry.value.rows = new_rows;
- break;
- }
- }
- }
- }
- container_grid.insert_row (idx);
+ return null;
}
private void on_container_grid_size_allocate (Allocation alloc) {
@@ -828,86 +212,57 @@ public class Contacts.ContactEditor : ContactForm {
}
}
- public HashMap<string, PropertyData?> properties_changed () {
- var props_set = new HashMap<string, PropertyData?> ();
-
- foreach (var entry in writable_personas.entries) {
- foreach (var field_entry in entry.value.entries) {
- if (field_entry.value.changed && !props_set.has_key (field_entry.key)) {
- PropertyData p = PropertyData ();
- p.persona = null;
- if (contact != null) {
- p.persona = contact.find_persona_from_uid (entry.key);
- }
-
- switch (field_entry.key) {
- case "email-addresses":
- p.value = get_value_from_emails (field_entry.value.rows);
- break;
- case "phone-numbers":
- p.value = get_value_from_phones (field_entry.value.rows);
- break;
- case "urls":
- p.value = get_value_from_urls (field_entry.value.rows);
- break;
- case "nickname":
- p.value = get_value_from_nickname (field_entry.value.rows);
- break;
- case "birthday":
- p.value = get_value_from_birthday (field_entry.value.rows);
- break;
- case "notes":
- p.value = get_value_from_notes (field_entry.value.rows);
- break;
- case "postal-addresses":
- p.value = get_value_from_addresses (field_entry.value.rows);
- break;
- }
-
- props_set.set (field_entry.key, p);
- }
- }
+ public async void save_changes () throws Error {
+ for (uint i = 0; i < this.fields.get_n_items (); i++) {
+ var field = this.fields.get_item (i) as EditableProperty;
+
+ if (field != null)
+ yield field.save_changes ();
}
- return props_set;
- }
+ if (name_changed ()) {
+ var v = get_full_name_value ();
+ yield this.contact.set_individual_property ("full-name", v);
+ /*XXX*/
+ /* display_name_changed (v.get_string ()); */
+ }
- public void add_new_row_for_property (Persona? p, string prop_name, string? type = null) {
- /* Somehow, I need to ensure that p is the main/default/first persona */
- Persona persona = null;
- if (contact != null) {
- if (p == null) {
- persona = new FakePersona (this.store, contact);
- writable_personas[persona.uid] = new HashMap<string, Field?> ();
- } else {
- persona = p;
- }
+ if (avatar_changed ()) {
+ var v = get_avatar_value ();
+ yield this.contact.set_individual_property ("avatar", v);
}
+ }
- int next_idx = 0;
- foreach (var fields in writable_personas.values) {
- if (fields.has_key (prop_name)) {
- foreach (var idx in fields[prop_name].rows.keys) {
- if (idx < last_row)
- next_idx = idx > next_idx ? idx : next_idx;
- }
- break;
- }
+ public HashTable<string, Value?> create_details_for_new_contact () {
+ var details = new HashTable<string, Value?> (str_hash, str_equal);
+
+ // Collect the details from the editor
+ if (name_changed ())
+ details["full-name"] = get_full_name_value ();
+
+ if (avatar_changed ())
+ details["avatar"] = get_avatar_value ();
+
+ for (uint i = 0; i < this.fields.get_n_items (); i++) {
+ var field = this.fields.get_item (i) as EditableProperty;
+
+ if (field != null)
+ details[field.property_name] = field.create_value ();
}
- next_idx = (next_idx == 0 ? last_row : next_idx) + 1;
- insert_row_at (next_idx);
- add_edit_row (persona, prop_name, ref next_idx, true, type);
- last_row++;
- container_grid.show_all ();
+
+ return details;
+ }
+
+ public void add_new_row_for_property (Persona? p, string prop_name, string? type = null) {
}
// Creates the contact's current avatar in a big button on top of the Editor
private void create_avatar_button () {
- this.avatar = new Avatar (PROFILE_SIZE, this.contact);
+ this.avatar_widget = new Avatar (PROFILE_SIZE, this.contact);
var button = new Button ();
button.get_accessible ().set_name (_("Change avatar"));
- button.image = this.avatar;
+ button.image = (Avatar) this.avatar_widget;
button.clicked.connect (on_avatar_button_clicked);
this.container_grid.attach (button, 0, 0, 1, 3);
@@ -917,8 +272,8 @@ public class Contacts.ContactEditor : ContactForm {
private void on_avatar_button_clicked (Button avatar_button) {
var popover = new AvatarSelector (avatar_button, this.contact);
popover.set_avatar.connect ( (icon) => {
- this.avatar.set_data ("value", icon);
- this.avatar.set_data ("changed", true);
+ this.avatar_widget.set_data ("value", icon);
+ this.avatar_widget.set_data ("changed", true);
Gdk.Pixbuf? a_pixbuf = null;
try {
@@ -927,17 +282,17 @@ public class Contacts.ContactEditor : ContactForm {
} catch {
}
- this.avatar.set_pixbuf (a_pixbuf);
+ ((Avatar) this.avatar_widget).set_pixbuf (a_pixbuf);
});
popover.show();
}
public bool avatar_changed () {
- return this.avatar.get_data<bool> ("changed");
+ return this.avatar_widget.get_data<bool> ("changed");
}
public Value get_avatar_value () {
- GLib.Icon icon = this.avatar.get_data<GLib.Icon> ("value");
+ GLib.Icon icon = this.avatar_widget.get_data<GLib.Icon> ("value");
Value v = Value (icon.get_type ());
v.set_object (icon);
return v;
@@ -945,30 +300,31 @@ public class Contacts.ContactEditor : ContactForm {
// Creates the big name entry on the top
private void create_name_entry () {
- this.name_entry = new Entry ();
- this.name_entry.hexpand = true;
- this.name_entry.valign = Align.CENTER;
- this.name_entry.placeholder_text = _("Add name");
- this.name_entry.set_data ("changed", false);
+ var name_entry = new Entry ();
+ name_entry.hexpand = true;
+ name_entry.valign = Align.CENTER;
+ name_entry.placeholder_text = _("Add name");
+ name_entry.set_data ("changed", false);
+ this.name_widget = name_entry;
if (this.contact != null)
- this.name_entry.text = this.contact.individual.display_name;
+ name_entry.text = this.contact.individual.display_name;
/* structured name change */
- this.name_entry.changed.connect (() => {
- this.name_entry.set_data ("changed", true);
+ name_entry.changed.connect (() => {
+ name_entry.set_data ("changed", true);
});
- this.container_grid.attach (this.name_entry, 1, 0, 3, 3);
+ this.container_grid.attach (name_entry, 1, 0, 3, 3);
}
public bool name_changed () {
- return this.name_entry.get_data<bool> ("changed");
+ return this.name_widget.get_data<bool> ("changed");
}
public Value get_full_name_value () {
Value v = Value (typeof (string));
- v.set_string (this.name_entry.get_text ());
+ v.set_string (((Entry) this.name_widget).text);
return v;
}
}
diff --git a/src/contacts-contact-form-field.vala b/src/contacts-contact-form-field.vala
new file mode 100644
index 0000000..a1ae7ba
--- /dev/null
+++ b/src/contacts-contact-form-field.vala
@@ -0,0 +1,916 @@
+/*
+ * Copyright (C) 2018 Niels De Graef <nielsdegraef 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 2 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/>.
+ */
+
+using Gtk;
+using Folks;
+using Gee;
+
+private class Contacts.PropertyFieldFactory {
+
+ // should_show_delegate
+ // create_delgeate
+
+ /* private struct FieldEntry { */
+ /* public delegate bool should_show (Persona? p); */
+ /* public delegate void create (Persona? p); */
+ /* } */
+
+ /* private Gee.Map<unowned string, FieldEntry> registered_entries */
+ /* = new Gee.HashMap<unowned string, FieldEntry> (); */
+
+ /* public void register (string property, ..., ...) { */
+ /* this.registered_entries.add (FieldEntry () { */
+ /* should_show, */
+ /* create */
+ /* }); */
+ /* } */
+}
+
+/**
+ * A PropertyField is an abstraction of the property of a Contact.
+ */
+public abstract class Contacts.PropertyField : Object {
+
+ public Persona? persona { get; construct set; default = null; }
+
+ public unowned string property_name { get; construct set; }
+
+// XXX need to call create_row() first
+ public PropertyWidget? row { get; private set; }
+
+ public abstract void create_widgets (PropertyWidget widget);
+
+ public Widget create_row (SizeGroup label_group,
+ SizeGroup value_group,
+ SizeGroup actions_group) {
+ this.row = new PropertyWidget (this, label_group, value_group, actions_group);
+ this.row.margin = 6;
+ this.row.hexpand = true;
+
+ // The subclass is responsible to make the appropriate widgets
+ create_widgets (row);
+
+ this.row.show_all ();
+
+ return this.row;
+ }
+}
+
+public interface Contacts.EditableProperty : PropertyField {
+
+ /* public bool dirty { get; construct set; } */
+
+ // NEEDED FOR NEW CONTACT CREATION
+ public abstract Value? create_value ();
+
+ // NEEDED FOR CHANGING EXISTING CONTACTS
+ public abstract async void save_changes () throws PropertyError;
+}
+
+public class Contacts.PropertyWidget : ListBoxRow {
+
+ private Grid grid = new Grid ();
+
+ private unowned SizeGroup labels_group;
+ private unowned SizeGroup values_group;
+ private unowned SizeGroup actions_group;
+
+// The parent field
+/// XXX maybe only store the persona?
+ public weak PropertyField field { get; construct set; }
+
+ construct {
+ this.selectable = false;
+ this.activatable = false;
+
+ this.grid.column_spacing = 12;
+ this.grid.row_spacing = 12;
+ this.grid.hexpand = true;
+ add (this.grid);
+ }
+
+ public PropertyWidget (PropertyField parent, SizeGroup labels, SizeGroup values, SizeGroup actions) {
+ Object (field: parent);
+
+ this.labels_group = labels;
+ this.values_group = values;
+ this.actions_group = actions;
+ }
+
+ // Get the latest row number. This might have changed due to e.g. deletion of some row
+ private int get_last_row_nr () {
+ int last_row = -1;
+ foreach (var child in this.grid.get_children ()) {
+ int top_attach;
+ this.grid.child_get (child, "top-attach", out top_attach);
+ last_row = int.max (last_row, top_attach);
+ }
+
+ return last_row;
+ }
+
+ public void add_row (Widget label, Widget value, Widget? actions = null) {
+ int row_nr = get_last_row_nr () + 1;
+ this.grid.attach (label, 0, row_nr);
+ this.grid.attach (value, 1, row_nr);
+ if (actions != null)
+ this.grid.attach (actions, 2, row_nr);
+
+ this.labels_group.add_widget (label);
+ this.values_group.add_widget (value);
+ if (actions != null)
+ this.actions_group.add_widget (actions);
+ }
+
+ // Buidler
+ // Up next are some
+ public Label create_type_label (string? text) {
+ var label = new Label (text ?? "");
+
+ label.xalign = 1.0f;
+ label.halign = Align.END;
+ label.valign = Align.START;
+ label.get_style_context ().add_class ("dim-label");
+
+ return label;
+ }
+
+ public Label create_value_label (string? text, bool use_markup = false) {
+ var label = new Label (text ?? "");
+ label.use_markup = use_markup;
+ label.set_line_wrap (true);
+ label.xalign = 0.0f;
+ label.set_halign (Align.START);
+ label.set_ellipsize (Pango.EllipsizeMode.END);
+ label.wrap_mode = Pango.WrapMode.CHAR;
+ label.set_selectable (true);
+
+ return label;
+ }
+
+ public Label create_value_link (string text, string url) {
+ var link = "<a href=\"%s\">%s</a>".printf (url, text);
+ return create_value_label (link, true);
+ }
+
+ public Entry create_value_entry (string? text) {
+ var value_entry = new Entry ();
+ value_entry.text = text;
+ value_entry.hexpand = true;
+
+ return value_entry;
+ }
+
+ public Button create_delete_button (string? description) {
+ var delete_button = new Button.from_icon_name ("user-trash-symbolic", IconSize.MENU);
+ delete_button.valign = Align.START;
+ delete_button.get_accessible ().set_name (description);
+ delete_button.clicked.connect ((button) => {
+ int top_attach;
+ this.grid.child_get (delete_button, "top-attach", out top_attach);
+ this.grid.remove_row (top_attach);
+ });
+ return delete_button;
+ }
+}
+
+public class Contacts.NicknameField : PropertyField {
+
+ protected string nickname = "";
+
+ public NicknameField (Persona persona) {
+ Object (
+ property_name: "nickname",
+ persona: persona
+ );
+
+ this.nickname = ((NameDetails) persona).nickname;
+ }
+
+ public static bool should_show (Persona persona) {
+ unowned NameDetails? details = persona as NameDetails;
+ return (details != null && details.nickname != "");
+ }
+
+ public override void create_widgets (PropertyWidget widget) {
+ var type_label = row.create_type_label (_("Nickname"));
+ var value_label = row.create_value_label (this.nickname);
+ widget.add_row (type_label, value_label);
+ }
+}
+
+public class Contacts.EditableNicknameField : NicknameField, EditableProperty {
+
+ public EditableNicknameField (Persona? persona) {
+ base (persona);
+ }
+
+ public EditableNicknameField.empty () {
+ Object (
+ property_name: "nickname",
+ persona: null
+ );
+ }
+
+ public override void create_widgets (PropertyWidget widget) {
+ var type_label = row.create_type_label (_("Nickname"));
+ var nickname_entry = row.create_value_entry (this.nickname);
+ var delete_button = row.create_delete_button (_("Remove nickname"));
+ widget.add_row (type_label, nickname_entry, delete_button);
+ }
+
+ public Value? create_value () {
+ if (this.nickname == "")
+ return null;
+
+ var new_value = Value (typeof (string));
+ new_value.set_string (this.nickname);
+ return new_value;
+ }
+
+ public async void save_changes () throws PropertyError {
+ assert (this.persona != null);
+
+ yield ((NameDetails) this.persona).change_nickname (this.nickname);
+ }
+}
+
+public class Contacts.BirthdayField : PropertyField {
+
+ // In local timezone
+ protected DateTime birthday;
+
+ public BirthdayField (Persona persona) {
+ Object (
+ property_name: "birthday",
+ persona: persona
+ );
+
+ unowned BirthdayDetails details = (BirthdayDetails) persona;
+ this.birthday = details.birthday.to_local ();
+ }
+
+ public static bool should_show (Persona persona) {
+ unowned BirthdayDetails? details = persona as BirthdayDetails;
+ return (details != null && details.birthday != null);
+ }
+
+ public override void create_widgets (PropertyWidget widget) {
+ var type_label = row.create_type_label (_("Birthday"));
+ var value_label = row.create_value_label (this.birthday.format ("%x"));
+ widget.add_row (type_label, value_label);
+ }
+}
+
+public class Contacts.EditableBirthdayField : BirthdayField, EditableProperty {
+
+ public EditableBirthdayField (Persona? persona) {
+ base (persona);
+ }
+
+ public EditableBirthdayField.empty () {
+ Object (
+ property_name: "birthday",
+ persona: null
+ );
+
+ this.birthday = new DateTime.now_local ();
+ }
+
+ public override void create_widgets (PropertyWidget widget) {
+ var type_label = row.create_type_label (_("Birthday"));
+ var birthday_entry = create_date_widget ();
+ var delete_button = row.create_delete_button (_("Remove birthday"));
+ widget.add_row (type_label, birthday_entry, delete_button);
+ }
+
+ private Widget create_date_widget () {
+ var box = new Grid ();
+ box.column_spacing = 12;
+
+ // Day
+ var day_spin = new SpinButton.with_range (1.0, 31.0, 1.0);
+ day_spin.set_digits (0);
+ day_spin.numeric = true;
+ day_spin.set_value (this.birthday.get_day_of_month ());
+ box.add (day_spin);
+
+ // Month
+ var month_combo = new ComboBoxText ();
+ var january = new DateTime.local (1, 1, 1, 1, 1, 1);
+ for (int i = 0; i < 12; i++) {
+ var month = january.add_months (i);
+ month_combo.append_text (month.format ("%B"));
+ }
+ month_combo.set_active (this.birthday.get_month () - 1);
+ month_combo.hexpand = true;
+ box.add (month_combo);
+
+ // Year
+ var year_spin = new SpinButton.with_range (1800, 3000, 1);
+ year_spin.set_digits (0);
+ year_spin.numeric = true;
+ year_spin.set_value (this.birthday.get_year ());
+ box.add (year_spin);
+
+ // We can't set the day/month/year directly, so calculate the diff and add that
+ day_spin.changed.connect (() => {
+ var diff = this.birthday.get_day_of_month () - day_spin.get_value_as_int ();
+ this.birthday.add_days (diff);
+ });
+ month_combo.changed.connect (() => {
+ adjust_date_range (year_spin, month_combo, day_spin);
+
+ var diff = this.birthday.get_month () - month_combo.get_active () - 1;
+ this.birthday.add_months (diff);
+ });
+ year_spin.changed.connect (() => {
+ adjust_date_range (year_spin, month_combo, day_spin);
+
+ var diff = this.birthday.get_year () - year_spin.get_value_as_int ();
+ this.birthday.add_years (diff);
+ });
+
+ return box;
+ }
+
+ // Make sure our user can't make an invalid date (e.g. February 31)
+ private void adjust_date_range (SpinButton year_spin, ComboBoxText month_combo, SpinButton day_spin) {
+ const int[] month_of_31 = {3, 5, 8, 10};
+ if (month_combo.get_active () in month_of_31) {
+ day_spin.set_range (1, 30);
+ } else if (month_combo.get_active () == 1) {
+ var year = (DateYear) year_spin.get_value_as_int ();
+ if (year.is_leap_year ())
+ day_spin.set_range (1, 29);
+ else
+ day_spin.set_range (1, 28);
+ }
+ }
+
+ public Value? create_value () {
+ var new_value = Value (typeof (DateTime));
+ new_value.set_boxed (this.birthday.to_utc ());
+ return new_value;
+ }
+
+ public async void save_changes () throws PropertyError {
+ assert (this.persona != null);
+
+ yield ((BirthdayDetails) this.persona).change_birthday (this.birthday);
+ }
+}
+
+public class Contacts.PhoneNrsField : PropertyField {
+
+ protected ArrayList<string> types = new ArrayList<string> ();
+ protected ArrayList<string> phone_nrs = new ArrayList<string> ();
+
+ public PhoneNrsField (Persona persona) {
+ Object (
+ property_name: "phone-numbers",
+ persona: persona
+ );
+
+ unowned PhoneDetails? details = this.persona as PhoneDetails;
+ foreach (var phone in details.phone_numbers) {
+ this.types.add (TypeSet.phone.format_type (phone));
+ this.phone_nrs.add (phone.value);
+ }
+ }
+
+ public static bool should_show (Persona persona) {
+ unowned PhoneDetails? details = persona as PhoneDetails;
+ return (details != null && !details.phone_numbers.is_empty);
+ }
+
+ public override void create_widgets (PropertyWidget widget) {
+ for (int i = 0; i < this.phone_nrs.size; i++)
+ add_field (this.types.get(i), this.phone_nrs.get(i), widget);
+ }
+
+ protected virtual void add_field (string type, string phone, PropertyWidget widget) {
+ var type_label = row.create_type_label (type);
+ var value_label = row.create_value_label (phone);
+/* #if HAVE_TELEPATHY */
+/* if (this.store.caller_account != null) { */
+/* var button = add_row_with_button (, phone.value); */
+/* button.clicked.connect (() => { */
+/* Utils.start_call (phone.value, this.store.caller_account); */
+/* }); */
+/* } else { */
+/* add_row_with_label (TypeSet.phone.format_type (phone), phone.value); */
+/* } */
+/* #else */
+/* add_row_with_label (TypeSet.phone.format_type (phone), phone.value); */
+/* #endif */
+ widget.add_row (type_label, value_label);
+ }
+}
+
+public class Contacts.EditablePhoneNrsField : PhoneNrsField, EditableProperty {
+
+ public EditablePhoneNrsField (Persona? persona) {
+ base (persona);
+ }
+
+ public EditablePhoneNrsField.empty () {
+ Object (
+ property_name: "phone-numbers",
+ persona: null
+ );
+
+ this.types.add ("");
+ this.phone_nrs.add ("");
+ }
+
+ protected override void add_field (string type, string phone, PropertyWidget widget) {
+ var type_label = row.create_type_label (type);
+ var entry = row.create_value_entry (phone);
+ var delete_button = row.create_delete_button (_("Remove phone number"));
+
+ widget.add_row (type_label, entry, delete_button);
+ }
+
+ public Value? create_value () {
+ if (this.phone_nrs.is_empty)
+ return null;
+
+ var new_details = create_set ();
+ // Check if we only had empty phone_nrs
+ if (new_details.is_empty)
+ return null;
+
+ var result = Value (new_details.get_type ());
+ result.set_object (new_details);
+ return result;
+ }
+
+ public async void save_changes () throws PropertyError {
+ assert (this.persona != null);
+
+ var new_addrs = create_set ();
+ yield ((PhoneDetails) this.persona).change_phone_numbers (new_addrs);
+ }
+
+ private HashSet<PhoneFieldDetails>? create_set () {
+ var new_details = new HashSet<PhoneFieldDetails> ();
+ for (int i = 0; i < this.phone_nrs.size; i++) {
+ if (this.phone_nrs[i] != "")
+ continue;
+
+ // XXX fix parameters here
+ var phone = new PhoneFieldDetails (this.phone_nrs[i], null);
+ new_details.add (phone);
+ }
+
+ return new_details;
+ }
+}
+
+public class Contacts.EmailsField : PropertyField {
+
+ protected ArrayList<string> types = new ArrayList<string> ();
+ protected ArrayList<string> emails = new ArrayList<string> ();
+
+ public EmailsField (Persona persona) {
+ Object (
+ property_name: "email-addresses",
+ persona: persona
+ );
+
+ unowned EmailDetails? details = persona as EmailDetails;
+ foreach (var email in details.email_addresses) {
+ this.types.add (TypeSet.email.format_type (email));
+ this.emails.add (email.value);
+ }
+ }
+
+ public static bool should_show (Persona persona) {
+ unowned EmailDetails? details = persona as EmailDetails;
+ return (details != null && !details.email_addresses.is_empty);
+ }
+
+ public override void create_widgets (PropertyWidget widget) {
+ for (int i = 0; i < this.emails.size; i++)
+ add_field (this.types[i], this.emails[i], widget);
+ }
+
+ protected virtual void add_field (string type, string email, PropertyWidget widget) {
+ var type_label = row.create_type_label (type);
+ var url = "mailto:" + Uri.escape_string (email, "@", false);
+ var value_label = row.create_value_link (email, url);
+ widget.add_row (type_label, value_label);
+ }
+}
+
+public class Contacts.EditableEmailsField : EmailsField, EditableProperty {
+
+ public EditableEmailsField (Persona? persona) {
+ base (persona);
+ }
+
+ public EditableEmailsField.empty () {
+ Object (
+ property_name: "email-addresses",
+ persona: null
+ );
+
+ this.types.add ("");
+ this.emails.add ("");
+ }
+
+ protected override void add_field (string type, string email, PropertyWidget widget) {
+ var type_label = row.create_type_label (type);
+ var entry = row.create_value_entry (email);
+ var delete_button = row.create_delete_button (_("Remove email address"));
+
+ widget.add_row (type_label, entry, delete_button);
+ }
+
+ public Value? create_value () {
+ if (this.emails.is_empty)
+ return null;
+
+ var new_details = create_set ();
+ // Check if we only had empty emails
+ if (new_details.is_empty)
+ return null;
+
+ var result = Value (new_details.get_type ());
+ result.set_object (new_details);
+ return result;
+ }
+
+ public async void save_changes () throws PropertyError {
+ assert (this.persona != null);
+
+ var new_addrs = create_set ();
+ yield ((EmailDetails) this.persona).change_email_addresses (new_addrs);
+ }
+
+ private HashSet<EmailFieldDetails>? create_set () {
+ var new_details = new HashSet<EmailFieldDetails> ();
+ for (int i = 0; i < this.emails.size; i++) {
+ if (this.emails[i] != "")
+ continue;
+
+ // XXX fix parameters here
+ var email = new EmailFieldDetails (this.emails[i], null);
+ new_details.add (email);
+ }
+
+ return new_details;
+ }
+}
+
+public class Contacts.UrlsField : PropertyField {
+
+ protected ArrayList<string> urls = new ArrayList<string> ();
+
+ public UrlsField (Persona persona) {
+ Object (
+ property_name: "urls",
+ persona: persona
+ );
+
+ unowned UrlDetails? details = persona as UrlDetails;
+ foreach (var url in details.urls)
+ this.urls.add (url.value);
+ }
+
+ public static bool should_show (Persona persona) {
+ unowned UrlDetails? details = persona as UrlDetails;
+ return (details != null && !details.urls.is_empty);
+ }
+
+ public override void create_widgets (PropertyWidget widget) {
+ for (int i = 0; i < urls.size; i++)
+ add_field (this.urls.get(i), widget);
+ }
+
+ protected virtual void add_field (string url, PropertyWidget widget) {
+ var type_label = row.create_type_label (_("Website"));
+ var url_link = Uri.escape_string (url, "@", false);
+ var value_label = row.create_value_link (url, url_link);
+
+ widget.add_row (type_label, value_label);
+ }
+}
+
+public class Contacts.EditableUrlsField : UrlsField, EditableProperty {
+
+ public EditableUrlsField (Persona? persona) {
+ base (persona);
+ }
+
+ public EditableUrlsField.empty () {
+ Object (
+ property_name: "urls",
+ persona: null
+ );
+
+ this.urls.add ("");
+ }
+
+ protected override void add_field (string url, PropertyWidget widget) {
+ var type_label = row.create_type_label (_("Website"));
+ var entry = row.create_value_entry (url);
+ var delete_button = row.create_delete_button (_("Remove website"));
+
+ widget.add_row (type_label, entry, delete_button);
+ }
+
+ public Value? create_value () {
+ if (this.urls.is_empty)
+ return null;
+
+ var new_details = create_set ();
+ // Check if we only had empty urls
+ if (new_details.is_empty)
+ return null;
+
+ var result = Value (new_details.get_type ());
+ result.set_object (new_details);
+ return result;
+ }
+
+ public async void save_changes () throws PropertyError {
+ assert (this.persona != null);
+
+ var new_urls = create_set ();
+ yield ((UrlDetails) this.persona).change_urls (new_urls);
+ }
+
+ private HashSet<UrlFieldDetails>? create_set () {
+ var new_details = new HashSet<UrlFieldDetails> ();
+ for (int i = 0; i < this.urls.size; i++) {
+ if (this.urls[i] != "")
+ continue;
+
+ // XXX fix parameters here
+ var url = new UrlFieldDetails (this.urls[i], null);
+ new_details.add (url);
+ }
+
+ return new_details;
+ }
+}
+
+public class Contacts.ImAddressesField : PropertyField {
+
+ public ImAddressesField (Persona persona) {
+ Object (
+ property_name: "im-addresses",
+ persona: persona
+ );
+ }
+
+ public static bool should_show (Persona persona) {
+ unowned ImDetails? details = persona as ImDetails;
+ return (details != null && details.im_addresses.size > 0);
+ }
+
+ public override void create_widgets (PropertyWidget widget) {
+ unowned ImDetails? details = this.persona as ImDetails;
+
+ var it = details.im_addresses.map_iterator ();
+ while (it.next ()) {
+ var protocol = it.get_key ();
+ var address = it.get_value ();
+ var type_label = row.create_type_label (ImService.get_display_name (protocol));
+ var value_label = row.create_value_label (address.value);
+ widget.add_row (type_label, value_label);
+ }
+ }
+}
+
+/* XXX */
+/* public class Contacts.EditableImAddressesField : EditableProperty { */
+/* } */
+
+public class Contacts.NotesField : PropertyField {
+
+ protected ArrayList<string> notes = new ArrayList<string> ();
+
+ public NotesField (Persona persona) {
+ Object (
+ property_name: "notes",
+ persona: persona
+ );
+
+ unowned NoteDetails? details = persona as NoteDetails;
+ foreach (var note in details.notes)
+ this.notes.add (note.value);
+ }
+
+ public static bool should_show (Persona persona) {
+ unowned NoteDetails? details = persona as NoteDetails;
+ return (details != null && !details.notes.is_empty);
+ }
+
+ public override void create_widgets (PropertyWidget widget) {
+ for (int i = 0; i < notes.size; i++)
+ add_field (this.notes.get(i), widget);
+ }
+
+ protected virtual void add_field (string note, PropertyWidget widget) {
+ var type_label = row.create_type_label (_("Note"));
+ var value_label = row.create_value_label (note);
+ widget.add_row (type_label, value_label);
+ }
+}
+
+public class Contacts.EditableNotesField : NotesField, EditableProperty {
+
+ public EditableNotesField (Persona? persona) {
+ base (persona);
+ }
+
+ public EditableNotesField.empty () {
+ Object (
+ property_name: "note-addresses",
+ persona: null
+ );
+
+ this.notes.add ("");
+ }
+
+ protected override void add_field (string note, PropertyWidget widget) {
+ var type_label = row.create_type_label (_("Note"));
+ var entry = row.create_value_entry (note);
+ var delete_button = row.create_delete_button (_("Remove note"));
+
+ widget.add_row (type_label, entry, delete_button);
+ }
+
+ public Value? create_value () {
+ if (this.notes.is_empty)
+ return null;
+
+ var new_details = create_set ();
+ // Check if we only had empty addresses
+ if (new_details.is_empty)
+ return null;
+
+ var result = Value (new_details.get_type ());
+ result.set_object (new_details);
+ return result;
+ }
+
+ public async void save_changes () throws PropertyError {
+ assert (this.persona != null);
+
+ var new_addrs = create_set ();
+ yield ((NoteDetails) this.persona).change_notes (new_addrs);
+ }
+
+ private HashSet<NoteFieldDetails>? create_set () {
+ var new_details = new HashSet<NoteFieldDetails> ();
+ for (int i = 0; i < this.notes.size; i++) {
+ if (this.notes[i] != "")
+ continue;
+
+ // XXX fix parameters here
+ var note = new NoteFieldDetails (this.notes[i], null);
+ new_details.add (note);
+ }
+
+ return new_details;
+ }
+}
+
+public class Contacts.PostalAddressesField : PropertyField {
+
+ protected ArrayList<string> types = new ArrayList<string> ();
+ protected ArrayList<PostalAddress> addresses = new ArrayList<PostalAddress> ();
+
+ public PostalAddressesField (Persona persona) {
+ Object (
+ property_name: "postal-addresses",
+ persona: persona
+ );
+
+ unowned PostalAddressDetails? details = persona as PostalAddressDetails;
+ foreach (var address in details.postal_addresses) {
+ this.types.add (TypeSet.general.format_type (address));
+ this.addresses.add (address.value);
+ }
+ }
+
+ public static bool should_show (Persona persona) {
+ unowned PostalAddressDetails? details = persona as PostalAddressDetails;
+ return (details != null && !details.postal_addresses.is_empty);
+ }
+
+ public override void create_widgets (PropertyWidget widget) {
+ for (int i = 0; i < addresses.size; i++)
+ add_field (this.types.get(i), this.addresses.get(i), widget);
+ }
+
+ protected virtual void add_field (string type, PostalAddress address, PropertyWidget widget) {
+ var type_label = row.create_type_label (type);
+ var all_strs = string.joinv ("\n", Contact.format_address (address));
+ var value_label = row.create_value_label (all_strs);
+ widget.add_row (type_label, value_label);
+ }
+}
+
+public class Contacts.EditablePostalAddressesField : PostalAddressesField, EditableProperty {
+
+ public const string[] POSTAL_ELEMENT_PROPS = { "street", "extension", "locality", "region", "postal_code",
"po_box", "country"};
+ public static string[] POSTAL_ELEMENT_NAMES = { _("Street"), _("Extension"), _("City"),
_("State/Province"), _("Zip/Postal Code"), _("PO box"), _("Country")};
+
+ public EditablePostalAddressesField (Persona? persona) {
+ base (persona);
+ }
+
+ public EditablePostalAddressesField.empty () {
+ Object (
+ property_name: "postal-addresses",
+ persona: null
+ );
+
+ this.types.add ("");
+ this.addresses.add (new PostalAddress (null, null, null, null, null, null, null, null, null));
+ }
+
+ protected override void add_field (string type, PostalAddress address, PropertyWidget widget) {
+ var type_label = row.create_type_label (type);
+ var grid = new Grid ();
+ grid.orientation = Orientation.VERTICAL;
+ for (int i = 0; i < POSTAL_ELEMENT_PROPS.length; i++) {
+ unowned string address_part = POSTAL_ELEMENT_PROPS[i];
+ string part;
+ address.get (address_part, out part);
+
+ var part_entry = widget.create_value_entry (part);
+ part_entry.placeholder_text = POSTAL_ELEMENT_NAMES[i];
+ grid.add (part_entry);
+ }
+ grid.show_all ();
+ var delete_button = row.create_delete_button (_("Remove postal address"));
+
+ widget.add_row (type_label, grid, delete_button);
+ }
+
+ public Value? create_value () {
+ if (this.addresses.is_empty)
+ return null;
+
+ var new_details = create_set ();
+ // Check if we only had empty addresses
+ if (new_details.is_empty)
+ return null;
+
+ var result = Value (new_details.get_type ());
+ result.set_object (new_details);
+ return result;
+ }
+
+ public async void save_changes () throws PropertyError {
+ assert (this.persona != null);
+
+ var new_addrs = create_set ();
+ yield ((PostalAddressDetails) this.persona).change_postal_addresses (new_addrs);
+ }
+
+ private HashSet<PostalAddressFieldDetails>? create_set () {
+ var new_details = new HashSet<PostalAddressFieldDetails> ();
+ for (int i = 0; i < this.addresses.size; i++) {
+ if (is_empty_postal_address (this.addresses[i]))
+ continue;
+
+ // XXX fix parameters here
+ var address = new PostalAddressFieldDetails (this.addresses[i], null);
+ new_details.add (address);
+ }
+
+ return new_details;
+ }
+
+ private bool is_empty_postal_address (PostalAddress addr) {
+ return addr.po_box == "" &&
+ addr.extension == "" &&
+ addr.street == "" &&
+ addr.locality == "" &&
+ addr.region == "" &&
+ addr.postal_code == "" &&
+ addr.country == "" &&
+ addr.address_format == "";
+ }
+}
diff --git a/src/contacts-contact-form.vala b/src/contacts-contact-form.vala
index f44a2fb..af07fdd 100644
--- a/src/contacts-contact-form.vala
+++ b/src/contacts-contact-form.vala
@@ -28,6 +28,9 @@ using Gtk;
*/
[GtkTemplate (ui = "/org/gnome/Contacts/ui/contacts-contact-form.ui")]
public abstract class Contacts.ContactForm : Grid {
+ // XXX make some kind of factory here
+ // Where PropertyFields can register themselves
+ // as (non-)editable for a given property
protected const string[] SORTED_PROPERTIES = {
"email-addresses",
@@ -49,31 +52,74 @@ public abstract class Contacts.ContactForm : Grid {
[GtkChild]
protected Grid container_grid;
- protected int last_row = 0;
+
+ [GtkChild]
+ protected ListBox form_container;
+ protected GLib.ListStore fields = new GLib.ListStore (typeof (PropertyField));
+
+ protected SizeGroup labels_sizegroup = new SizeGroup (SizeGroupMode.HORIZONTAL);
+ protected SizeGroup values_sizegroup = new SizeGroup (SizeGroupMode.HORIZONTAL);
+ protected SizeGroup actions_sizegroup = new SizeGroup (SizeGroupMode.HORIZONTAL);
+
+ // Seperate treatment for the header widgets
+ protected Widget avatar_widget;
+ protected Widget name_widget;
construct {
this.container_grid.set_focus_vadjustment (this.main_sw.get_vadjustment ());
this.main_sw.get_style_context ().add_class ("contacts-contact-form");
+
+ this.form_container.bind_model (fields, create_row);
+ this.form_container.set_header_func (create_persona_store_header);
}
- protected string[] sort_persona_properties (string[] props) {
- CompareDataFunc<string> compare_properties = (a, b) => {
- foreach (var prop in SORTED_PROPERTIES) {
- if (a == prop)
- return (b == prop)? 0 : -1;
+ private Gtk.Widget create_row (Object object) {
+ return ((PropertyField) object).create_row (labels_sizegroup, values_sizegroup, actions_sizegroup);
+ }
+
+ public void create_persona_store_header (ListBoxRow row, ListBoxRow? before) {
+ // Leave out the persona store header at the start
+ if (before == null) {
+ row.set_header (null);
+ return;
+ }
+
+ PropertyWidget current = (PropertyWidget) row;
+ PropertyWidget previous = (PropertyWidget) before;
+ if (current.field.persona == null || previous.field.persona == null)
+ return;
+ if (current.field.persona == previous.field.persona)
+ return;
+
+ var label = create_persona_store_label (current.field.persona);
+ row.set_header (label);
+ }
- if (b == prop)
- return 1;
- }
+ private static int compare_fields (Object obj_a, Object obj_b) {
+ unowned PropertyField a = (PropertyField) obj_a;
+ unowned PropertyField b = (PropertyField) obj_b;
- return 0;
- };
+ return compare_properties (a.property_name, b.property_name);
+ }
+
+ private static int compare_properties (string a, string b) {
+ foreach (var prop in SORTED_PROPERTIES) {
+ if (a == prop)
+ return (b == prop)? 0 : -1;
+
+ if (b == prop)
+ return 1;
+ }
+ return 0;
+ }
+
+ protected string[] sort_persona_properties (string[] props) {
var sorted_props = new ArrayList<string> ();
foreach (var s in props)
sorted_props.add (s);
- sorted_props.sort ((owned) compare_properties);
+ sorted_props.sort (compare_properties);
return sorted_props.to_array ();
}
@@ -84,7 +130,22 @@ public abstract class Contacts.ContactForm : Grid {
store_name.set_halign (Align.START);
store_name.xalign = 0.0f;
store_name.margin_start = 6;
+ store_name.visible = true;
return store_name;
}
+
+ protected PropertyField? get_property_field_for_name (string name) {
+ for (uint i = 0; i < this.fields.get_n_items(); i++) {
+ var field = (PropertyField) this.fields.get_item (i);
+ if (field.property_name == name)
+ return field;
+ }
+
+ return null;
+ }
+
+ protected void add_field (PropertyField field) {
+ this.fields.insert_sorted (field, compare_fields);
+ }
}
diff --git a/src/contacts-contact-pane.vala b/src/contacts-contact-pane.vala
index 1fabeff..6b2856f 100644
--- a/src/contacts-contact-pane.vala
+++ b/src/contacts-contact-pane.vala
@@ -223,8 +223,15 @@ public class Contacts.ContactPane : Stack {
this.on_edit_mode = false;
/* saving changes */
- if (!drop_changes)
- save_editor_changes.begin ();
+ if (!drop_changes) {
+ this.editor.save_changes.begin ((obj, res) => {
+ try {
+ this.editor.save_changes.end (res);
+ } catch (Error e) {
+ show_message (e.message);
+ }
+ });
+ }
remove_contact_editor ();
@@ -234,35 +241,6 @@ public class Contacts.ContactPane : Stack {
set_visible_child (this.none_selected_page);
}
- private async void save_editor_changes () {
- foreach (var prop in this.editor.properties_changed ().entries) {
- try {
- yield Contact.set_persona_property (prop.value.persona, prop.key, prop.value.value);
- } catch (Error e) {
- show_message (e.message);
- }
- }
-
- if (this.editor.name_changed ()) {
- var v = this.editor.get_full_name_value ();
- try {
- yield this.contact.set_individual_property ("full-name", v);
- display_name_changed (v.get_string ());
- } catch (Error e) {
- show_message (e.message);
- }
- }
-
- if (this.editor.avatar_changed ()) {
- var v = this.editor.get_avatar_value ();
- try {
- yield this.contact.set_individual_property ("avatar", v);
- } catch (Error e) {
- show_message (e.message);
- }
- }
- }
-
public void new_contact () {
this.on_edit_mode = true;
this.contact = null;
@@ -273,17 +251,7 @@ public class Contacts.ContactPane : Stack {
// Creates a new contact from the details in the ContactEditor
public async void create_contact () {
- var details = new HashTable<string, Value?> (str_hash, str_equal);
-
- // Collect the details from the editor
- if (editor.name_changed ())
- details["full-name"] = this.editor.get_full_name_value ();
-
- if (editor.avatar_changed ())
- details["avatar"] = this.editor.get_avatar_value ();
-
- foreach (var prop in this.editor.properties_changed ().entries)
- details[prop.key] = prop.value.value;
+ var details = this.editor.create_details_for_new_contact ();
// Leave edit mode
stop_editing (true);
diff --git a/src/contacts-contact-sheet.vala b/src/contacts-contact-sheet.vala
index 62f2e3f..ef1beaf 100644
--- a/src/contacts-contact-sheet.vala
+++ b/src/contacts-contact-sheet.vala
@@ -37,233 +37,100 @@ public class Contacts.ContactSheet : ContactForm {
update ();
}
- private Button add_row_with_button (string label, string value, bool use_link_button = false) {
- var type_label = new Label (label);
- type_label.xalign = 1.0f;
- type_label.set_halign (Align.END);
- type_label.get_style_context ().add_class ("dim-label");
- this.container_grid.attach (type_label, 0, this.last_row);
-
- var value_button = use_link_button? new LinkButton (value) : new Button.with_label (value);
- value_button.focus_on_click = false;
- value_button.relief = ReliefStyle.NONE;
- value_button.halign = Align.START;
- this.container_grid.attach (value_button, 1, this.last_row);
- this.last_row++;
-
- (value_button.get_child () as Label).set_ellipsize (Pango.EllipsizeMode.END);
- (value_button.get_child () as Label).wrap_mode = Pango.WrapMode.CHAR;
-
- return value_button;
- }
-
- void add_row_with_label (string label_value, string value) {
- var type_label = new Label (label_value);
- type_label.xalign = 1.0f;
- type_label.set_halign (Align.END);
- type_label.set_valign (Align.START);
- type_label.get_style_context ().add_class ("dim-label");
- this.container_grid.attach (type_label, 0, this.last_row, 1, 1);
-
- var value_label = new Label (value);
- value_label.set_line_wrap (true);
- value_label.xalign = 0.0f;
- value_label.set_halign (Align.START);
- value_label.set_ellipsize (Pango.EllipsizeMode.END);
- value_label.wrap_mode = Pango.WrapMode.CHAR;
- value_label.set_selectable (true);
-
- /* FIXME: hardcode gap to match the button size */
- type_label.margin_top = 3;
- value_label.margin_start = 6;
- value_label.margin_top = 3;
- value_label.margin_bottom = 3;
-
- this.container_grid.attach (value_label, 1, this.last_row, 1, 1);
- this.last_row++;
- }
-
private void update () {
- this.last_row = 0;
- this.container_grid.foreach ((child) => this.container_grid.remove (child));
+ clear_previous_details ();
- var image_frame = new Avatar (PROFILE_SIZE, this.contact);
- image_frame.set_vexpand (false);
- image_frame.set_valign (Align.START);
- this.container_grid.attach (image_frame, 0, 0, 1, 3);
+ this.avatar_widget = new Avatar (PROFILE_SIZE, this.contact);
+ this.avatar_widget.set_vexpand (false);
+ this.avatar_widget.set_valign (Align.START);
+ this.container_grid.attach (this.avatar_widget, 0, 0, 1, 3);
create_name_label ();
- this.last_row += 3; // Name/Avatar takes up 3 rows
-
var personas = this.contact.get_personas_for_display ();
/* Cause personas are sorted properly I can do this */
foreach (var p in personas) {
- bool is_first_persona = (this.last_row == 3);
- int persona_store_pos = this.last_row;
- if (!is_first_persona) {
- this.container_grid.attach (create_persona_store_label (p), 0, this.last_row, 3);
- this.last_row++;
- }
-
- foreach (var prop in ContactForm.SORTED_PROPERTIES)
- add_row_for_property (p, prop);
-
- // Nothing to show in the persona: don't mention it
- bool is_empty_persona = (this.last_row == persona_store_pos + 1);
- if (!is_first_persona && is_empty_persona) {
- this.container_grid.remove_row (persona_store_pos);
- this.last_row--;
+ foreach (var prop in ContactForm.SORTED_PROPERTIES) {
+ var field = add_row_for_property (p, prop);
+ if (field != null)
+ add_field (field);
}
}
show_all ();
}
- private void update_name_label (Gtk.Label name_label) {
+ private void clear_previous_details () {
+ if (this.avatar_widget != null)
+ this.avatar_widget.destroy ();
+ this.avatar_widget = null;
+
+ if (this.name_widget != null)
+ this.name_widget.destroy ();
+ this.name_widget = null;
+
+ this.fields.remove_all ();
+ }
+
+ private void update_name_widget () {
var name = Markup.printf_escaped ("<span font='16'>%s</span>",
this.contact.individual.display_name);
- name_label.set_markup (name);
+ ((Label) this.name_widget).set_markup (name);
}
private void create_name_label () {
var name_label = new Label ("");
name_label.ellipsize = Pango.EllipsizeMode.END;
name_label.xalign = 0f;
+ name_label.hexpand = true;
name_label.selectable = true;
this.container_grid.attach (name_label, 1, 0, 1, 3);
- update_name_label (name_label);
+ this.name_widget = name_label;
+ update_name_widget ();
this.contact.individual.notify["display-name"].connect ((obj, spec) => {
- update_name_label (name_label);
+ update_name_widget ();
});
}
- private void add_row_for_property (Persona persona, string property) {
- switch (property) {
+ private PropertyField? add_row_for_property (Persona persona, string property_name) {
+ switch (property_name) {
case "email-addresses":
- add_emails (persona);
+ if (EmailsField.should_show (persona))
+ return new EmailsField (persona);
break;
case "phone-numbers":
- add_phone_nrs (persona);
+ if (PhoneNrsField.should_show (persona))
+ return new PhoneNrsField (persona);
break;
case "im-addresses":
- add_im_addresses (persona);
+ if (ImAddressesField.should_show (persona))
+ return new ImAddressesField (persona);
break;
case "urls":
- add_urls (persona);
+ if (UrlsField.should_show (persona))
+ return new UrlsField (persona);
break;
case "nickname":
- add_nickname (persona);
+ if (NicknameField.should_show (persona))
+ return new NicknameField (persona);
break;
case "birthday":
- add_birthday (persona);
+ if (BirthdayField.should_show (persona))
+ return new BirthdayField (persona);
break;
case "notes":
- add_notes (persona);
+ if (NotesField.should_show (persona))
+ return new NotesField (persona);
break;
case "postal-addresses":
- add_postal_addresses (persona);
+ if (PostalAddressesField.should_show (persona))
+ return new PostalAddressesField (persona);
break;
default:
- debug ("Unsupported property: %s", property);
+ debug ("Unsupported property: %s", property_name);
break;
}
- }
- private void add_emails (Persona persona) {
- var details = persona as EmailDetails;
- if (details != null) {
- var emails = Contact.sort_fields<EmailFieldDetails>(details.email_addresses);
- foreach (var email in emails) {
- var button = add_row_with_button (TypeSet.email.format_type (email), email.value);
- button.clicked.connect (() => {
- Utils.compose_mail ("%s <%s>".printf(this.contact.individual.display_name, email.value));
- });
- }
- }
- }
-
- private void add_phone_nrs (Persona persona) {
- var phone_details = persona as PhoneDetails;
- if (phone_details != null) {
- var phones = Contact.sort_fields<PhoneFieldDetails>(phone_details.phone_numbers);
- foreach (var phone in phones) {
-#if HAVE_TELEPATHY
- if (this.store.caller_account != null) {
- var button = add_row_with_button (TypeSet.phone.format_type (phone), phone.value);
- button.clicked.connect (() => {
- Utils.start_call (phone.value, this.store.caller_account);
- });
- } else {
- add_row_with_label (TypeSet.phone.format_type (phone), phone.value);
- }
-#else
- add_row_with_label (TypeSet.phone.format_type (phone), phone.value);
-#endif
- }
- }
- }
-
- private void add_im_addresses (Persona persona) {
-#if HAVE_TELEPATHY
- var im_details = persona as ImDetails;
- if (im_details != null) {
- foreach (var protocol in im_details.im_addresses.get_keys ()) {
- foreach (var id in im_details.im_addresses[protocol]) {
- if (persona is Tpf.Persona) {
- var button = add_row_with_button (ImService.get_display_name (protocol), id.value);
- button.clicked.connect (() => {
- var im_persona = this.contact.find_im_persona (protocol, id.value);
- if (im_persona != null) {
- var type = im_persona.presence_type;
- if (type != PresenceType.UNSET && type != PresenceType.ERROR &&
- type != PresenceType.OFFLINE && type != PresenceType.UNKNOWN) {
- Utils.start_chat (this.contact, protocol, id.value);
- }
- }
- });
- }
- }
- }
- }
-#endif
- }
-
- private void add_urls (Persona persona) {
- var url_details = persona as UrlDetails;
- if (url_details != null) {
- foreach (var url in url_details.urls)
- add_row_with_button (_("Website"), url.value, true);
- }
- }
-
- private void add_nickname (Persona persona) {
- var name_details = persona as NameDetails;
- if (name_details != null && is_set (name_details.nickname))
- add_row_with_label (_("Nickname"), name_details.nickname);
- }
-
- private void add_birthday (Persona persona) {
- var birthday_details = persona as BirthdayDetails;
- if (birthday_details != null && birthday_details.birthday != null)
- add_row_with_label (_("Birthday"), birthday_details.birthday.to_local ().format ("%x"));
- }
-
- private void add_notes (Persona persona) {
- var note_details = persona as NoteDetails;
- if (note_details != null) {
- foreach (var note in note_details.notes)
- add_row_with_label (_("Note"), note.value);
- }
- }
-
- private void add_postal_addresses (Persona persona) {
- var addr_details = persona as PostalAddressDetails;
- if (addr_details != null) {
- foreach (var addr in addr_details.postal_addresses) {
- var all_strs = string.joinv ("\n", Contact.format_address (addr.value));
- add_row_with_label (TypeSet.general.format_type (addr), all_strs);
- }
- }
+ return null;
}
}
diff --git a/src/contacts-utils.vala b/src/contacts-utils.vala
index 5552e8c..7e207c2 100644
--- a/src/contacts-utils.vala
+++ b/src/contacts-utils.vala
@@ -70,15 +70,6 @@ namespace Contacts {
}
namespace Contacts.Utils {
- public void compose_mail (string email) {
- var mailto_uri = "mailto:" + Uri.escape_string (email, "@" , false);
- try {
- Gtk.show_uri_on_window (null, mailto_uri, 0);
- } catch (Error e) {
- debug ("Couldn't launch URI \"%s\": %s", mailto_uri, e.message);
- }
- }
-
#if HAVE_TELEPATHY
public void start_chat (Contact contact, string protocol, string id) {
var im_persona = contact.find_im_persona (protocol, id);
diff --git a/src/meson.build b/src/meson.build
index f3e69c6..d6ed4ee 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -78,6 +78,7 @@ contacts_vala_sources = files(
'contacts-avatar-selector.vala',
'contacts-contact-editor.vala',
'contacts-contact-form.vala',
+ 'contacts-contact-form-field.vala',
'contacts-contact-list.vala',
'contacts-contact-pane.vala',
'contacts-contact-sheet.vala',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]