summaryrefslogtreecommitdiff
path: root/src/contacts-contact-editor.vala
diff options
context:
space:
mode:
authorJulian Sparber <julian@sparber.net>2020-01-10 15:46:17 +0100
committerNiels De Graef <nielsdegraef@gmail.com>2020-02-03 13:19:03 +0100
commit368d11f94f411a63ab5792ebed55047ef7bcf0fd (patch)
tree27aa357647ec4195759afd1e8270ab0832b82d01 /src/contacts-contact-editor.vala
parentaa2433307d485b020b8630601dbd1e91b569cea9 (diff)
downloadgnome-contacts-368d11f94f411a63ab5792ebed55047ef7bcf0fd.tar.gz
Editor: use listbox layout to edit contact and secondary menu
GNOME uses now listboxes as the standart design pattern instead of a grid. This replaces the grid and makes use of listboxes to allow the user to edit a contact. Some key features are: - Hide less important properties when not used - Dynamically fill the editor with properties so that the user has always one empty row to fill for each visible property - use a dialog for the birthday picker - Group properties by persona ContactSheet: Replace the edit button with a secondary menu. The secondary menu contains share (hidden for now), edit, unlink and delete. The reason for this change is that it doesn't make a lot of sense to have delete and unlink inside the edit mode, since they don't require to commit changed. Folks doesn't provied a staging features. So changes are commited directly to the backend. The FakePersona and FakeIndividual are used exactly for this. They work as a intermidiate layer so the editor can change the persona directly and then when the user presses "done" the changes can be copied to the real contact.
Diffstat (limited to 'src/contacts-contact-editor.vala')
-rw-r--r--src/contacts-contact-editor.vala941
1 files changed, 30 insertions, 911 deletions
diff --git a/src/contacts-contact-editor.vala b/src/contacts-contact-editor.vala
index 396a681..c73da5d 100644
--- a/src/contacts-contact-editor.vala
+++ b/src/contacts-contact-editor.vala
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2011 Alexander Larsson <alexl@redhat.com>
+ * Copyright (C) 2019 Purism SPC
*
* 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
@@ -19,885 +20,32 @@ using Gtk;
using Folks;
using Gee;
-public class Contacts.AddressEditor : Box {
- public Entry? entries[7]; /* must be the number of elements in postal_element_props */
- public PostalAddressFieldDetails details;
-
- 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 signal void changed ();
-
- public AddressEditor (PostalAddressFieldDetails _details) {
- set_hexpand (true);
- set_orientation (Orientation.VERTICAL);
-
- details = _details;
-
- for (int i = 0; i < entries.length; i++) {
- string postal_part;
- details.value.get (AddressEditor.postal_element_props[i], out postal_part);
-
- entries[i] = new Entry ();
- entries[i].set_hexpand (true);
- entries[i].set ("placeholder-text", AddressEditor.postal_element_names[i]);
-
- if (postal_part != null)
- entries[i].set_text (postal_part);
-
- entries[i].get_style_context ().add_class ("contacts-postal-entry");
- add (entries[i]);
-
- entries[i].changed.connect (() => {
- changed ();
- });
- }
- }
-
- public override void grab_focus () {
- entries[0].grab_focus ();
- }
-}
-
/**
* A widget that allows the user to edit a given {@link Contact}.
*/
-[GtkTemplate (ui = "/org/gnome/Contacts/ui/contacts-contact-editor.ui")]
-public class Contacts.ContactEditor : ContactForm {
-
- private const string[] DEFAULT_PROPS_NEW_CONTACT = {
- "email-addresses.personal",
- "phone-numbers.cell",
- "postal-addresses.home"
- };
-
- private weak Widget focus_widget;
-
+public class Contacts.ContactEditor : Box {
+ private Individual individual;
private Entry name_entry;
-
+ private AvatarSelector avatar_selector = null;
private Avatar avatar;
- [GtkChild]
- private MenuButton add_detail_button;
-
- [GtkChild]
- public Button linked_button;
-
- [GtkChild]
- public Button remove_button;
-
- public struct PropertyData {
- Persona? persona;
- Value value;
- }
-
- struct RowData {
- AbstractFieldDetails details;
- }
-
- struct Field {
- bool changed;
- HashMap<int, RowData?> rows;
- }
-
- private HashSet<Persona> unlink_personas;
- /* 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;
- }
-
- public bool has_nickname_row {
- get; private set; default = false;
- }
-
- public bool has_notes_row {
- get; private set; default = false;
- }
-
- construct {
- this.unlink_personas = new HashSet<Persona> ();
- this.writable_personas = new HashMap<string, HashMap<string, Field?>> ();
- this.container_grid.size_allocate.connect(on_container_grid_size_allocate);
- }
-
- public ContactEditor (Individual? individual, Store store, GLib.ActionGroup editor_actions) {
- this.store = store;
+ public ContactEditor (Individual individual, IndividualAggregator aggregator) {
+ Object (orientation: Orientation.VERTICAL, spacing: 24);
this.individual = individual;
- this.add_detail_button.get_popover ().insert_action_group ("edit", editor_actions);
-
- if (individual != null) {
- this.remove_button.sensitive = Contacts.Utils.can_remove_personas (individual);
- this.linked_button.sensitive = individual.personas.size > 1;
- } else {
- this.remove_button.hide ();
- this.linked_button.hide ();
- }
-
- create_avatar_button ();
- create_name_entry ();
-
- if (individual != null)
- fill_in_contact ();
- else
- fill_in_empty ();
-
- this.container_grid.show_all ();
- }
-
- private void fill_in_contact () {
- int i = 3;
- int last_store_position = 0;
- bool is_first_persona = true;
-
- var personas = Contacts.Utils.get_personas_for_display (individual);
- 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;
- }
+ Box header = new Box (Orientation.HORIZONTAL, 6);
+ header.add (create_avatar_button ());
+ header.add (create_name_entry ());
+ add (header);
- 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 p in individual.personas) {
+ add (new EditorPersona (p, aggregator));
}
- }
-
- 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.active_descriptor.save_to_field_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.active_descriptor.save_to_field_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.active_descriptor.save_to_field_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 (2, 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_from_field_details (details);
- if (type != null)
- combo.set_active_from_vcard_type (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, 2, row, 1, 1);
-
- /* Notify change to upper layer */
- combo.changed.connect ((c) => {
- 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));
- });
-
- 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, 2, 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, 2, 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"));
- }
- 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, 2, 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;
- });
- }
-
- 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_from_field_details (details);
- if (type != null)
- combo.set_active_from_vcard_type (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, 2, 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";
- 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 = Contacts.Utils.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 });
- }
- }
- 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 = Contacts.Utils.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 });
- }
- }
- 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 });
- }
- }
- 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 (2, 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 });
- }
- }
- 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 });
- }
- 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 });
- }
- }
- 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 });
- }
- }
- 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);
- }
-
- private void on_container_grid_size_allocate (Allocation alloc) {
- if (this.focus_widget != null && this.focus_widget is Widget) {
- this.focus_widget.grab_focus ();
- this.focus_widget = null;
- }
- }
-
- 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 (individual != null) {
- p.persona = Contacts.Utils.find_persona_from_uid (individual, 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);
- }
- }
- }
-
- return props_set;
- }
-
- public HashSet<Persona> get_unlink_personas () {
- return unlink_personas;
- }
-
- public void add_new_row_for_property (Persona? persona, string prop_name, string? type = null) {
- 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;
- }
- }
- 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 ();
+ show_all ();
}
// Creates the contact's current avatar in a big button on top of the Editor
- private void create_avatar_button () {
+ private Widget create_avatar_button () {
this.avatar = new Avatar (PROFILE_SIZE, this.individual);
var button = new Button ();
@@ -905,65 +53,36 @@ public class Contacts.ContactEditor : ContactForm {
button.image = this.avatar;
button.clicked.connect (on_avatar_button_clicked);
- this.container_grid.attach (button, 0, 0, 1, 3);
+ return button;
}
// Show the avatar popover when the avatar is clicked
private void on_avatar_button_clicked (Button avatar_button) {
- var popover = new AvatarSelector (avatar_button, this.individual);
- popover.set_avatar.connect ( (icon) => {
- this.avatar.set_data ("value", icon);
- this.avatar.set_data ("changed", true);
-
- Gdk.Pixbuf? a_pixbuf = null;
- try {
- var stream = (icon as LoadableIcon).load (PROFILE_SIZE, null);
- a_pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, PROFILE_SIZE, PROFILE_SIZE, true);
- } catch {
- }
-
- this.avatar.set_pixbuf (a_pixbuf);
- });
- popover.show();
- }
-
- public bool avatar_changed () {
- return this.avatar.get_data<bool> ("changed");
- }
-
- public Value get_avatar_value () {
- GLib.Icon icon = this.avatar.get_data<GLib.Icon> ("value");
- Value v = Value (icon.get_type ());
- v.set_object (icon);
- return v;
+ if (this.avatar_selector == null)
+ this.avatar_selector = new AvatarSelector (avatar_button, this.individual);
+ this.avatar_selector.show();
}
// Creates the big name entry on the top
- private void create_name_entry () {
+ private Widget create_name_entry () {
+ NameDetails name = this.individual as NameDetails;
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);
- if (this.individual != null)
- this.name_entry.text = this.individual.display_name;
+ // Get primary persona from this.individual
+ this.name_entry.text = name.full_name;
- /* structured name change */
this.name_entry.changed.connect (() => {
- this.name_entry.set_data ("changed", true);
- });
-
- this.container_grid.attach (this.name_entry, 1, 0, 2, 3);
- }
-
- public bool name_changed () {
- return this.name_entry.get_data<bool> ("changed");
- }
+ foreach (var p in this.individual.personas) {
+ var name_p = p as NameDetails;
+ if (name_p != null) {
+ name_p.full_name = this.name_entry.get_text ();
+ }
+ }
+ });
- public Value get_full_name_value () {
- Value v = Value (typeof (string));
- v.set_string (this.name_entry.get_text ());
- return v;
+ return this.name_entry;
}
}