Compare commits
1 commit
master
...
wip/nielsd
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ffb57199f6 |
18 changed files with 1307 additions and 1139 deletions
|
@ -3,7 +3,7 @@ AC_INIT([gnome-contacts],[3.25.4],[http://bugzilla.gnome.org/enter_bug.cgi?produ
|
|||
AC_CONFIG_SRCDIR([src/main.vala])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AM_INIT_AUTOMAKE([foreign tar-ustar dist-xz no-dist-gzip -Wno-portability])
|
||||
AM_INIT_AUTOMAKE([foreign tar-ustar dist-xz no-dist-gzip -Wno-portability subdir-objects])
|
||||
|
||||
# Enable silent rules is available
|
||||
AM_SILENT_RULES([yes])
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
</item>
|
||||
</menu>
|
||||
|
||||
<template class="ContactsContactEditor" parent="GtkGrid">
|
||||
<template class="ContactsEditorContactEditor" parent="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
|
|
|
@ -10,7 +10,7 @@ AM_CPPFLAGS = \
|
|||
$(NULL)
|
||||
|
||||
AM_VALAFLAGS = \
|
||||
--vapidir=../vapi --pkg config --pkg custom \
|
||||
--vapidir=../vapi --pkg config --pkg custom --debug \
|
||||
@CONTACTS_PACKAGES@ \
|
||||
--target-glib=$(GLIB_REQUIRED) --gresources=$(top_srcdir)/data/contacts.gresource.xml \
|
||||
$(NULL)
|
||||
|
@ -23,11 +23,24 @@ endif
|
|||
bin_PROGRAMS = gnome-contacts
|
||||
|
||||
vala_sources = \
|
||||
editor/contacts-editor-addresses-editor.vala \
|
||||
editor/contacts-editor-avatar-editor.vala \
|
||||
editor/contacts-editor-birthday-editor.vala \
|
||||
editor/contacts-editor-composite-editor.vala \
|
||||
editor/contacts-editor-contact-editor.vala \
|
||||
editor/contacts-editor-details-editor-factory.vala \
|
||||
editor/contacts-editor-details-editor.vala \
|
||||
editor/contacts-editor-emails-editor.vala \
|
||||
editor/contacts-editor-full-name-editor.vala \
|
||||
editor/contacts-editor-nickname-editor.vala \
|
||||
editor/contacts-editor-notes-editor.vala \
|
||||
editor/contacts-editor-phones-editor.vala \
|
||||
editor/contacts-editor-urls-editor.vala \
|
||||
\
|
||||
contacts-app.vala \
|
||||
contacts-address-map.vala \
|
||||
contacts-contact.vala \
|
||||
contacts-contact-sheet.vala \
|
||||
contacts-contact-editor.vala \
|
||||
contacts-contact-pane.vala \
|
||||
contacts-types.vala \
|
||||
contacts-in-app-notification.vala \
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -57,7 +57,7 @@ public class Contacts.ContactPane : Stack {
|
|||
|
||||
[GtkChild]
|
||||
private Box contact_editor_page;
|
||||
private ContactEditor editor;
|
||||
private Editor.ContactEditor editor;
|
||||
|
||||
private SimpleActionGroup edit_contact_actions;
|
||||
private const GLib.ActionEntry[] action_entries = {
|
||||
|
@ -238,7 +238,7 @@ public class Contacts.ContactPane : Stack {
|
|||
|
||||
/* edit mode widgetry, third page */
|
||||
this.on_edit_mode = false;
|
||||
this.editor = new ContactEditor (this.edit_contact_actions);
|
||||
this.editor = new Editor.ContactEditor (this.edit_contact_actions);
|
||||
this.editor.linked_button.clicked.connect (linked_accounts);
|
||||
this.editor.remove_button.clicked.connect (delete_contact);
|
||||
this.contact_editor_page.add (this.editor);
|
||||
|
@ -268,11 +268,12 @@ public class Contacts.ContactPane : Stack {
|
|||
void on_add_detail (GLib.SimpleAction action, GLib.Variant? parameter) {
|
||||
var tok = action.name.split (".");
|
||||
|
||||
if (tok[0] == "add") {
|
||||
editor.add_new_row_for_property (contact.find_primary_persona (),
|
||||
tok[1],
|
||||
tok.length > 2 ? tok[2].up () : null);
|
||||
}
|
||||
// XXX
|
||||
/* if (tok[0] == "add") { */
|
||||
/* editor.add_new_row_for_property (contact.find_primary_persona (), */
|
||||
/* tok[1], */
|
||||
/* tok.length > 2 ? tok[2].up () : null); */
|
||||
/* } */
|
||||
}
|
||||
|
||||
void linked_accounts () {
|
||||
|
@ -300,72 +301,40 @@ public class Contacts.ContactPane : Stack {
|
|||
return;
|
||||
|
||||
if (on_edit) {
|
||||
if (contact == null) {
|
||||
return;
|
||||
}
|
||||
if (this.contact == null)
|
||||
return;
|
||||
|
||||
on_edit_mode = true;
|
||||
this.on_edit_mode = true;
|
||||
|
||||
sheet.clear ();
|
||||
this.sheet.clear ();
|
||||
|
||||
if (suggestion_grid != null) {
|
||||
suggestion_grid.destroy ();
|
||||
suggestion_grid = null;
|
||||
this.suggestion_grid.destroy ();
|
||||
this.suggestion_grid = null;
|
||||
}
|
||||
|
||||
editor.clear ();
|
||||
editor.edit (contact);
|
||||
editor.show_all ();
|
||||
this.editor.clear ();
|
||||
this.editor.edit (contact);
|
||||
this.editor.show_all ();
|
||||
set_visible_child (this.contact_editor_page);
|
||||
} else {
|
||||
on_edit_mode = false;
|
||||
this.on_edit_mode = false;
|
||||
/* saving changes */
|
||||
if (!drop_changes) {
|
||||
foreach (var prop in editor.properties_changed ().entries) {
|
||||
Contact.set_persona_property.begin (prop.value.persona, prop.key, prop.value.value,
|
||||
(obj, result) => {
|
||||
try {
|
||||
Contact.set_persona_property.end (result);
|
||||
} catch (Error e2) {
|
||||
App.app.show_message (e2.message);
|
||||
update_sheet ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (editor.name_changed ()) {
|
||||
var v = editor.get_full_name_value ();
|
||||
Contact.set_individual_property.begin (contact,
|
||||
"full-name", v,
|
||||
(obj, result) => {
|
||||
try {
|
||||
Contact.set_individual_property.end (result);
|
||||
} catch (Error e) {
|
||||
App.app.show_message (e.message);
|
||||
/* FIXME: add this back */
|
||||
/* l.set_markup (Markup.printf_escaped ("<span font='16'>%s</span>", contact.display_name)); */
|
||||
}
|
||||
});
|
||||
}
|
||||
if (editor.avatar_changed ()) {
|
||||
var v = editor.get_avatar_value ();
|
||||
Contact.set_individual_property.begin (contact,
|
||||
"avatar", v,
|
||||
(obj, result) => {
|
||||
try {
|
||||
Contact.set_individual_property.end (result);
|
||||
} catch (GLib.Error e) {
|
||||
App.app.show_message (e.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.editor.save_changes.begin ( (obj, res) => {
|
||||
try {
|
||||
this.editor.save_changes.end (res);
|
||||
} catch (Error e) {
|
||||
App.app.show_message (e.message);
|
||||
update_sheet ();
|
||||
}
|
||||
});
|
||||
}
|
||||
this.editor.clear ();
|
||||
|
||||
editor.clear ();
|
||||
|
||||
if (contact != null) {
|
||||
sheet.clear ();
|
||||
sheet.update (contact);
|
||||
if (this.contact != null) {
|
||||
this.sheet.clear ();
|
||||
this.sheet.update (contact);
|
||||
set_visible_child (this.contact_sheet_page);
|
||||
} else {
|
||||
set_visible_child (this.none_selected_page);
|
||||
|
@ -390,48 +359,19 @@ 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);
|
||||
var contacts_store = App.app.contacts_store;
|
||||
|
||||
// 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;
|
||||
|
||||
// Leave edit mode
|
||||
set_edit_mode (false, true);
|
||||
|
||||
if (details.size () == 0) {
|
||||
show_message_dialog (_("You need to enter some data"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (contacts_store.aggregator.primary_store == null) {
|
||||
show_message_dialog (_("No primary addressbook configured"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the contact
|
||||
var primary_store = contacts_store.aggregator.primary_store;
|
||||
Persona? persona = null;
|
||||
try {
|
||||
persona = yield Contact.create_primary_persona_for_details (primary_store, details);
|
||||
var contact = yield this.editor.save_changes ();
|
||||
// Now show it to the user
|
||||
if (contact != null)
|
||||
App.app.show_contact (contact);
|
||||
else
|
||||
show_message_dialog (_("Unable to find newly created contact"));
|
||||
} catch (Error e) {
|
||||
show_message_dialog (_("Unable to create new contacts: %s").printf (e.message));
|
||||
return;
|
||||
}
|
||||
|
||||
// Now show it to the user
|
||||
var contact = contacts_store.find_contact_with_persona (persona);
|
||||
if (contact != null)
|
||||
App.app.show_contact (contact);
|
||||
else
|
||||
show_message_dialog (_("Unable to find newly created contact"));
|
||||
}
|
||||
|
||||
private void show_message_dialog (string message) {
|
||||
|
|
119
src/editor/contacts-editor-addresses-editor.vala
Normal file
119
src/editor/contacts-editor-addresses-editor.vala
Normal file
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 Folks;
|
||||
using Gee;
|
||||
using Gtk;
|
||||
|
||||
public class Contacts.Editor.AddressesEditor : CompositeEditor<PostalAddressDetails, PostalAddressFieldDetails> {
|
||||
|
||||
public override string persona_property {
|
||||
get { return "postal-addresses"; }
|
||||
}
|
||||
|
||||
public AddressesEditor (PostalAddressDetails? details = null) {
|
||||
if (details != null) {
|
||||
var address_fields = Contact.sort_fields<PostalAddressFieldDetails>(details.postal_addresses);
|
||||
foreach (var address_field_detail in address_fields)
|
||||
this.child_editors.add (new AddressEditor (this, address_field_detail));
|
||||
} else {
|
||||
// No addresss were passed on => make a blank home address
|
||||
this.child_editors.add (new AddressEditor (this, null, "HOME"));
|
||||
}
|
||||
}
|
||||
|
||||
public override async void save (PostalAddressDetails address_details) throws PropertyError {
|
||||
yield address_details.change_postal_addresses (aggregate_children ());
|
||||
}
|
||||
|
||||
public class AddressEditor : Object, CompositeEditorChild<PostalAddressFieldDetails> {
|
||||
private TypeCombo type_combo;
|
||||
private Box address_widget;
|
||||
private Button delete_button;
|
||||
|
||||
public Entry? entries[7]; /* must be the number of elements in postal_element_props */
|
||||
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 AddressEditor (AddressesEditor parent, PostalAddressFieldDetails? details = null, string? type = null) {
|
||||
this.type_combo = parent.create_type_combo (TypeSet.general, details);
|
||||
this.type_combo.valign = Gtk.Align.START;
|
||||
this.address_widget = create_address_widget (parent);
|
||||
this.delete_button = parent.create_delete_button ();
|
||||
this.delete_button.valign = Gtk.Align.START;
|
||||
|
||||
if (details != null && details.value != null) {
|
||||
var address = details.value;
|
||||
this.entries[0].text = address.street ?? "";
|
||||
this.entries[1].text = address.extension ?? "";
|
||||
this.entries[2].text = address.locality ?? "";
|
||||
this.entries[3].text = address.region ?? "";
|
||||
this.entries[4].text = address.postal_code ?? "";
|
||||
this.entries[5].text = address.po_box ?? "";
|
||||
this.entries[6].text = address.country ?? "";
|
||||
}
|
||||
if (type != null)
|
||||
this.type_combo.set_to (type);
|
||||
}
|
||||
|
||||
public int attach_to_grid (Grid container_grid, int row) {
|
||||
container_grid.attach (this.type_combo, 0, row);
|
||||
container_grid.attach (this.address_widget, 1, row);
|
||||
container_grid.attach (this.delete_button, 2, row);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public PostalAddressFieldDetails create_details () {
|
||||
var address = new PostalAddress (
|
||||
this.entries[5].text, // po_box
|
||||
this.entries[1].text, // extension
|
||||
this.entries[0].text, // street
|
||||
this.entries[2].text, // locality
|
||||
this.entries[3].text, // region
|
||||
this.entries[4].text, // postal_code
|
||||
this.entries[6].text, // country
|
||||
"derp?", // XXX
|
||||
"");
|
||||
// XXX parameters
|
||||
return new PostalAddressFieldDetails (address, null);
|
||||
}
|
||||
|
||||
private Box create_address_widget (AddressesEditor parent) {
|
||||
var address_box = new Box(Orientation.VERTICAL, 0);
|
||||
address_box.hexpand = true;
|
||||
address_box.show ();
|
||||
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
string? postal_part = null;
|
||||
/* details.value.get (POSTAL_ELEMENT_PROPS[i], out postal_part); */
|
||||
|
||||
entries[i] = parent.create_entry ();
|
||||
entries[i].placeholder_text = POSTAL_ELEMENT_NAMES[i];
|
||||
|
||||
if (postal_part != null)
|
||||
entries[i].text = postal_part;
|
||||
|
||||
entries[i].get_style_context ().add_class ("contacts-entry");
|
||||
entries[i].get_style_context ().add_class ("contacts-postal-entry");
|
||||
address_box.add (entries[i]);
|
||||
}
|
||||
|
||||
return address_box;
|
||||
}
|
||||
}
|
||||
}
|
87
src/editor/contacts-editor-avatar-editor.vala
Normal file
87
src/editor/contacts-editor-avatar-editor.vala
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 Folks;
|
||||
using Gee;
|
||||
using Gtk;
|
||||
|
||||
public class Contacts.Editor.AvatarEditor : DetailsEditor<AvatarDetails> {
|
||||
|
||||
private Contact? contact;
|
||||
|
||||
private ContactFrame avatar_frame;
|
||||
|
||||
private LoadableIcon? avatar_icon = null;
|
||||
|
||||
public override string persona_property {
|
||||
get { return "avatar"; }
|
||||
}
|
||||
|
||||
public AvatarEditor (Contact? contact = null, AvatarDetails? details = null) {
|
||||
this.contact = contact;
|
||||
|
||||
this.avatar_frame = new ContactFrame (PROFILE_SIZE, true);
|
||||
this.avatar_frame.vexpand = false;
|
||||
this.avatar_frame.valign = Align.START;
|
||||
(this.avatar_frame.get_child () as Button).relief = ReliefStyle.NORMAL;
|
||||
this.avatar_frame.clicked.connect (on_avatar_frame_clicked);
|
||||
|
||||
if (contact != null) {
|
||||
this.avatar_frame.set_image (contact.individual, contact);
|
||||
contact.keep_widget_uptodate (this.avatar_frame, (w) => {
|
||||
this.avatar_frame.set_image (contact.individual, contact);
|
||||
});
|
||||
} else {
|
||||
this.avatar_frame.set_image (null, null);
|
||||
}
|
||||
this.avatar_frame.show_all ();
|
||||
}
|
||||
|
||||
public override int attach_to_grid (Grid container_grid, int row) {
|
||||
container_grid.attach (this.avatar_frame, 0, row, 1, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override async void save (AvatarDetails avatar_details) throws PropertyError {
|
||||
yield avatar_details.change_avatar (this.avatar_icon);
|
||||
}
|
||||
|
||||
public override Value create_value () {
|
||||
Value v = Value (this.avatar_icon.get_type ());
|
||||
v.set_object (this.avatar_icon);
|
||||
return v;
|
||||
}
|
||||
|
||||
// Show the avatar dialog when the avatar is clicked
|
||||
private void on_avatar_frame_clicked () {
|
||||
var dialog = new AvatarDialog (this.contact);
|
||||
dialog.set_avatar.connect ( (icon) => {
|
||||
this.avatar_icon = icon as LoadableIcon;
|
||||
this.dirty = 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_frame.set_pixbuf (a_pixbuf);
|
||||
});
|
||||
dialog.run ();
|
||||
}
|
||||
}
|
143
src/editor/contacts-editor-birthday-editor.vala
Normal file
143
src/editor/contacts-editor-birthday-editor.vala
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 Folks;
|
||||
using Gee;
|
||||
using Gtk;
|
||||
|
||||
public class Contacts.Editor.BirthdayEditor : DetailsEditor<BirthdayDetails> {
|
||||
private Label label;
|
||||
|
||||
private Grid date_grid;
|
||||
private SpinButton day_spin;
|
||||
private ComboBoxText month_combo;
|
||||
private SpinButton year_spin;
|
||||
|
||||
private Button delete_button;
|
||||
|
||||
public override string persona_property {
|
||||
get { return "birthday"; }
|
||||
}
|
||||
|
||||
/**
|
||||
* The day of the month (ranging from 1 to 31, depending on the month)
|
||||
*/
|
||||
private int day {
|
||||
get { return this.day_spin.get_value_as_int (); }
|
||||
set { this.day_spin.set_value (value); }
|
||||
}
|
||||
|
||||
/**
|
||||
* The month (ranging from 1 to 12)
|
||||
*/
|
||||
private int month {
|
||||
get { return this.month_combo.get_active () + 1; }
|
||||
set { this.month_combo.set_active (value - 1); }
|
||||
}
|
||||
|
||||
/**
|
||||
* The year
|
||||
*/
|
||||
private int year {
|
||||
get { return this.year_spin.get_value_as_int (); }
|
||||
set { this.year_spin.set_value (value); }
|
||||
}
|
||||
|
||||
public BirthdayEditor (BirthdayDetails? details = null) {
|
||||
DateTime date;
|
||||
if (details != null && details.birthday != null)
|
||||
date = details.birthday.to_local ();
|
||||
else
|
||||
date = new DateTime.now_local ();
|
||||
|
||||
this.label = create_label (_("Birthday"));
|
||||
this.date_grid = create_date_widget (date);
|
||||
this.delete_button = create_delete_button ();
|
||||
|
||||
this.day = date.get_day_of_month ();
|
||||
this.month = date.get_month ();
|
||||
this.year = date.get_year ();
|
||||
set_day_spin_range ();
|
||||
}
|
||||
|
||||
public override int attach_to_grid (Grid container_grid, int row) {
|
||||
container_grid.attach (this.label, 0, row);
|
||||
container_grid.attach (this.date_grid, 1, row);
|
||||
container_grid.attach (this.delete_button, 2, row);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public override async void save (BirthdayDetails birthday_details) throws PropertyError {
|
||||
yield birthday_details.change_birthday (create_datetime ().to_utc ());
|
||||
}
|
||||
|
||||
public override Value create_value () {
|
||||
var result = Value (typeof (DateTime));
|
||||
result.set_boxed (create_datetime ().to_utc ());
|
||||
return result;
|
||||
}
|
||||
|
||||
private DateTime create_datetime () {
|
||||
return new DateTime.local (this.year, this.month, this.day, 0, 0, 0);
|
||||
}
|
||||
|
||||
private Grid create_date_widget (DateTime? date) {
|
||||
var date_grid = new Grid ();
|
||||
date_grid.column_spacing = 12;
|
||||
|
||||
// Day
|
||||
this.day_spin = new SpinButton.with_range (1.0, 31.0, 1.0);
|
||||
this.day_spin.digits = 0;
|
||||
this.day_spin.numeric = true;
|
||||
this.day_spin.changed.connect ( () => { this.dirty = true; });
|
||||
date_grid.add (day_spin);
|
||||
|
||||
// Month
|
||||
this.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);
|
||||
this.month_combo.append_text (month.format ("%B"));
|
||||
}
|
||||
this.month_combo.get_style_context ().add_class ("contacts-combo");
|
||||
this.month_combo.hexpand = true;
|
||||
this.month_combo.changed.connect ( () => {
|
||||
this.dirty = true;
|
||||
set_day_spin_range ();
|
||||
});
|
||||
date_grid.add (month_combo);
|
||||
|
||||
// Year
|
||||
this.year_spin = new SpinButton.with_range (1800, 3000, 1);
|
||||
this.year_spin.digits = 0;
|
||||
this.year_spin.numeric = true;
|
||||
this.year_spin.changed.connect ( () => {
|
||||
this.dirty = true;
|
||||
set_day_spin_range ();
|
||||
});
|
||||
date_grid.add (year_spin);
|
||||
|
||||
date_grid.show_all ();
|
||||
return date_grid;
|
||||
}
|
||||
|
||||
private void set_day_spin_range () {
|
||||
var days_in_month = Date.get_days_in_month ((DateMonth) this.month, (DateYear) this.year);
|
||||
this.day_spin.set_range (1, days_in_month);
|
||||
}
|
||||
}
|
64
src/editor/contacts-editor-composite-editor.vala
Normal file
64
src/editor/contacts-editor-composite-editor.vala
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 Folks;
|
||||
using Gee;
|
||||
using Gtk;
|
||||
|
||||
/**
|
||||
* An interface for DetailsEditors that contain multiple child Element.
|
||||
* It has a ChildDetails type (C), for the Details a child widget represents
|
||||
*/
|
||||
public abstract class Contacts.Editor.CompositeEditor<D, C> : DetailsEditor<D> {
|
||||
|
||||
protected Gee.List<CompositeEditorChild<C>> child_editors = new LinkedList<CompositeEditorChild<C>> ();
|
||||
|
||||
public override int attach_to_grid (Grid container_grid, int start_row) {
|
||||
var current_row = start_row;
|
||||
foreach (var child_editor in this.child_editors)
|
||||
current_row += child_editor.attach_to_grid (container_grid, current_row);
|
||||
|
||||
return current_row - start_row;
|
||||
}
|
||||
|
||||
public override Value create_value () {
|
||||
var children = aggregate_children ();
|
||||
var val = Value (children.get_type ());
|
||||
val.set_object (children);
|
||||
return val;
|
||||
}
|
||||
|
||||
protected HashSet<C> aggregate_children () {
|
||||
var children = new HashSet<C> ();
|
||||
foreach (var child_editor in this.child_editors)
|
||||
children.add (child_editor.create_details ());
|
||||
return children;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A child to a CompositeEditor.
|
||||
*/
|
||||
public interface Contacts.Editor.CompositeEditorChild<D> : Object {
|
||||
|
||||
/**
|
||||
* Creates the details for this CompositeEditorChild, based on the (edited) values.
|
||||
*/
|
||||
public abstract D create_details ();
|
||||
|
||||
public abstract int attach_to_grid (Grid container_grid, int start_row);
|
||||
}
|
241
src/editor/contacts-editor-contact-editor.vala
Normal file
241
src/editor/contacts-editor-contact-editor.vala
Normal file
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Alexander Larsson <alexl@redhat.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;
|
||||
|
||||
public errordomain Contacts.Editor.SaveError {
|
||||
EMPTY_DATA,
|
||||
NO_PRIMARY_ADDRESSBOOK,
|
||||
}
|
||||
|
||||
[GtkTemplate (ui = "/org/gnome/contacts/ui/contacts-contact-editor.ui")]
|
||||
public class Contacts.Editor.ContactEditor : Grid {
|
||||
|
||||
private const string[] DEFAULT_PROPS_NEW_CONTACT = {
|
||||
"email-addresses",
|
||||
"phone-numbers",
|
||||
"postal-addresses"
|
||||
};
|
||||
|
||||
// We have a form with fields for each persona.
|
||||
private struct Form {
|
||||
Persona? persona; // null iff new contact
|
||||
Gee.List<DetailsEditor> editors;
|
||||
}
|
||||
|
||||
private Contact? contact;
|
||||
|
||||
private Grid container_grid;
|
||||
|
||||
[GtkChild]
|
||||
private ScrolledWindow main_sw;
|
||||
|
||||
[GtkChild]
|
||||
private MenuButton add_detail_button;
|
||||
|
||||
[GtkChild]
|
||||
public Button linked_button;
|
||||
|
||||
[GtkChild]
|
||||
public Button remove_button;
|
||||
|
||||
// The first row of the container_grid that is empty.
|
||||
private int next_row = 0;
|
||||
|
||||
private Gee.List<Form?> forms = new LinkedList<Form?> ();
|
||||
|
||||
private DetailsEditorFactory details_editor_factory = new DetailsEditorFactory ();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public ContactEditor (SimpleActionGroup editor_actions) {
|
||||
var hcenter = new Center ();
|
||||
hcenter.max_width = 600;
|
||||
hcenter.xalign = 0.0;
|
||||
|
||||
this.container_grid = new Grid ();
|
||||
this.container_grid.row_spacing = 12;
|
||||
this.container_grid.column_spacing = 12;
|
||||
this.container_grid.vexpand = true;
|
||||
this.container_grid.hexpand = true;
|
||||
this.container_grid.margin = 36;
|
||||
this.container_grid.margin_bottom = 24;
|
||||
|
||||
hcenter.add (this.container_grid);
|
||||
this.main_sw.add (hcenter);
|
||||
this.container_grid.set_focus_vadjustment (this.main_sw.get_vadjustment ());
|
||||
|
||||
this.main_sw.get_child ().get_style_context ().add_class ("contacts-main-view");
|
||||
this.main_sw.get_child ().get_style_context ().add_class ("view");
|
||||
|
||||
this.main_sw.show_all ();
|
||||
|
||||
this.add_detail_button.get_popover ().insert_action_group ("edit", editor_actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the ContactEditor to the given contact.
|
||||
* Use clear() to make sure nothing is lingering from the previous one.
|
||||
*/
|
||||
public void edit (Contact c) {
|
||||
this.contact = c;
|
||||
|
||||
this.remove_button.show ();
|
||||
this.linked_button.show ();
|
||||
this.remove_button.sensitive = this.contact.can_remove_personas ();
|
||||
this.linked_button.sensitive = this.contact.individual.personas.size > 1;
|
||||
|
||||
bool first_persona = true;
|
||||
foreach (var persona in c.get_personas_for_display ()) {
|
||||
add_widgets_for_persona (persona, first_persona);
|
||||
first_persona = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the ContactEditor for a new contact.
|
||||
* Use clear() to make sure nothing is lingering from the previous one.
|
||||
*/
|
||||
public void set_new_contact () {
|
||||
this.contact = null;
|
||||
|
||||
this.remove_button.hide ();
|
||||
this.linked_button.hide ();
|
||||
|
||||
add_widgets_for_persona (null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the widgets for the details in a persona
|
||||
*/
|
||||
private void add_widgets_for_persona (Persona? p, bool first_persona) {
|
||||
var form = Form ();
|
||||
form.persona = p;
|
||||
form.editors = new ArrayList<DetailsEditor> ();
|
||||
this.forms.add (form);
|
||||
|
||||
if (first_persona) {
|
||||
create_avatar_frame (form);
|
||||
create_name_entry (form);
|
||||
this.next_row += 3;
|
||||
} else {
|
||||
// Don't show the name on the default persona
|
||||
var store_name = new Label (Contact.format_persona_store_name_for_contact (p));
|
||||
store_name.halign = Align.START;
|
||||
store_name.xalign = 0.0f;
|
||||
store_name.margin_start = 6;
|
||||
this.container_grid.attach (store_name, 0, this.next_row, 2);
|
||||
this.next_row++;
|
||||
}
|
||||
|
||||
string[] writeable_props;
|
||||
if (p != null)
|
||||
writeable_props = Contact.sort_persona_properties (p.writeable_properties);
|
||||
else
|
||||
writeable_props = DEFAULT_PROPS_NEW_CONTACT;
|
||||
|
||||
foreach (var prop in writeable_props)
|
||||
add_property (form, prop);
|
||||
}
|
||||
|
||||
private void add_property (Form form, string prop_name) {
|
||||
var editor = this.details_editor_factory.create_details_editor (form.persona, prop_name);
|
||||
if (editor != null) {
|
||||
form.editors.add (editor);
|
||||
var rows_added = editor.attach_to_grid (this.container_grid, this.next_row);
|
||||
this.next_row += rows_added;
|
||||
}
|
||||
}
|
||||
|
||||
public void clear () {
|
||||
foreach (var w in container_grid.get_children ()) {
|
||||
w.destroy ();
|
||||
}
|
||||
this.forms.clear ();
|
||||
|
||||
remove_button.set_sensitive (false);
|
||||
linked_button.set_sensitive (false);
|
||||
|
||||
/* clean metadata as well */
|
||||
has_birthday_row = false;
|
||||
has_nickname_row = false;
|
||||
has_notes_row = false;
|
||||
|
||||
/* writable_personas.clear (); */
|
||||
contact = null;
|
||||
}
|
||||
|
||||
// Creates the contact's current avatar, the big frame on top of the Editor
|
||||
private void create_avatar_frame (Form form) {
|
||||
var avatar_editor = new AvatarEditor (this.contact, form.persona as AvatarDetails);
|
||||
avatar_editor.attach_to_grid (this.container_grid, 0);
|
||||
form.editors.add (avatar_editor);
|
||||
}
|
||||
|
||||
// Creates the big name entry on the top
|
||||
private void create_name_entry (Form form) {
|
||||
var full_name_editor = new FullNameEditor (this.contact, form.persona as NameDetails);
|
||||
full_name_editor.attach_to_grid (this.container_grid, 0);
|
||||
form.editors.add (full_name_editor);
|
||||
}
|
||||
|
||||
public async Contact save_changes () throws Error {
|
||||
if (this.contact == null) {
|
||||
var details = new HashTable<string, Value?> (str_hash, str_equal);
|
||||
var contacts_store = App.app.contacts_store;
|
||||
|
||||
//XXX check if name is filled in
|
||||
var form = this.forms[0];
|
||||
foreach (var details_editor in form.editors)
|
||||
if (details_editor.dirty)
|
||||
details[details_editor.persona_property] = details_editor.create_value ();
|
||||
|
||||
if (details.size () != 0)
|
||||
throw new SaveError.EMPTY_DATA (_("You need to enter some data"));
|
||||
|
||||
if (contacts_store.aggregator.primary_store == null)
|
||||
throw new SaveError.NO_PRIMARY_ADDRESSBOOK (_("No primary addressbook configured"));
|
||||
|
||||
// Create the contact
|
||||
var primary_store = contacts_store.aggregator.primary_store;
|
||||
var persona = yield Contact.create_primary_persona_for_details (primary_store, details);
|
||||
|
||||
return contacts_store.find_contact_with_persona (persona);
|
||||
}
|
||||
|
||||
//XXX check for empty values
|
||||
foreach (var form in this.forms) {
|
||||
foreach (var details_editor in form.editors) {
|
||||
if (details_editor.dirty)
|
||||
yield details_editor.save_to_persona (form.persona);
|
||||
}
|
||||
}
|
||||
return this.contact;
|
||||
}
|
||||
}
|
67
src/editor/contacts-editor-details-editor-factory.vala
Normal file
67
src/editor/contacts-editor-details-editor-factory.vala
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 Folks;
|
||||
using Gee;
|
||||
using Gtk;
|
||||
|
||||
/**
|
||||
* A Factory for DetailEditors.
|
||||
*/
|
||||
public class Contacts.Editor.DetailsEditorFactory : Object {
|
||||
|
||||
/**
|
||||
* Creates a DetailEditor for a specific property, given a persona.
|
||||
* @return The newly created editor, or null if no editor was created.
|
||||
*/
|
||||
public DetailsEditor? create_details_editor (Persona? p, string prop_name) {
|
||||
DetailsEditor? editor = null;
|
||||
|
||||
switch (prop_name) {
|
||||
case "birthday":
|
||||
var birthday_details = p as BirthdayDetails;
|
||||
if (p == null || birthday_details.birthday != null)
|
||||
editor = new BirthdayEditor (p as BirthdayDetails);
|
||||
break;
|
||||
case "email-addresses":
|
||||
editor = new EmailsEditor (p as EmailDetails);
|
||||
break;
|
||||
case "nickname":
|
||||
var name_details = p as NameDetails;
|
||||
if (p == null || (name_details.nickname != null && name_details.nickname != ""))
|
||||
editor = new NicknameEditor (name_details);
|
||||
break;
|
||||
case "notes":
|
||||
editor = new NotesEditor (p as NoteDetails);
|
||||
break;
|
||||
case "phone-numbers":
|
||||
editor = new PhonesEditor (p as PhoneDetails);
|
||||
break;
|
||||
case "postal-addresses":
|
||||
editor = new AddressesEditor (p as PostalAddressDetails);
|
||||
break;
|
||||
case "urls":
|
||||
editor = new UrlsEditor (p as UrlDetails);
|
||||
break;
|
||||
default:
|
||||
debug ("Unsupported property name \"%s\"", prop_name);
|
||||
break;
|
||||
}
|
||||
|
||||
return editor;
|
||||
}
|
||||
}
|
126
src/editor/contacts-editor-details-editor.vala
Normal file
126
src/editor/contacts-editor-details-editor.vala
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 Folks;
|
||||
using Gee;
|
||||
using Gtk;
|
||||
|
||||
/**
|
||||
* A DetailsEditor is an Element that can handle a specific property of a Persona.
|
||||
*/
|
||||
public abstract class Contacts.Editor.DetailsEditor<D> : Object {
|
||||
|
||||
/**
|
||||
* Fired when the user asks to remove the EditorElement.
|
||||
*/
|
||||
public signal void removed ();
|
||||
|
||||
/**
|
||||
* Returns whether the DetailsEditor has unsaved changes.
|
||||
*/
|
||||
public bool dirty { get; protected set; default = false; }
|
||||
|
||||
/**
|
||||
* Returns the Persona property (well, the string) this EditorElement takes care of.
|
||||
*/
|
||||
public abstract string persona_property { get; }
|
||||
|
||||
/**
|
||||
* Attaches the element to the grid (possibly over multiple rows).
|
||||
*
|
||||
* @param container_grid The grid to which the element should be added.
|
||||
* @param start_row The row at which we should start editing.
|
||||
*
|
||||
* @return The amount of rows that were added to the grid by this EditorElement.
|
||||
*/
|
||||
public abstract int attach_to_grid (Grid container_grid, int start_row);
|
||||
|
||||
/**
|
||||
* Saves the (edited) value to the Details object.
|
||||
*/
|
||||
public abstract async void save (D details) throws PropertyError;
|
||||
|
||||
public async void save_to_persona (Persona persona) throws PropertyError {
|
||||
yield save ((D) persona);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Value that can be used for methods like Folks.PersonaStore.add_persona_from_details()
|
||||
*/
|
||||
public abstract Value create_value ();
|
||||
|
||||
/* Helper methods for building
|
||||
----------------------------- */
|
||||
public TypeCombo create_type_combo (TypeSet type_set, AbstractFieldDetails? details = null) {
|
||||
var combo = new TypeCombo (type_set);
|
||||
combo.hexpand = false;
|
||||
if (details != null)
|
||||
combo.set_active (details);
|
||||
combo.valign = Align.CENTER; // XXX why not START?
|
||||
combo.changed.connect (() => { this.dirty = true; });
|
||||
combo.show ();
|
||||
|
||||
return combo;
|
||||
}
|
||||
|
||||
public Label create_label (string text) {
|
||||
var label = new Label (text);
|
||||
label.hexpand = false;
|
||||
label.halign = Align.START;
|
||||
label.margin_end = 6;
|
||||
label.show ();
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
public Entry create_entry () {
|
||||
var entry = new Entry ();
|
||||
entry.hexpand = true;
|
||||
entry.changed.connect (() => { this.dirty = true; });
|
||||
entry.show ();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
// XXX scrolledwindow?
|
||||
public ScrolledWindow create_textview () {
|
||||
var sw = new ScrolledWindow (null, null);
|
||||
sw.shadow_type = ShadowType.OUT;
|
||||
sw.set_size_request (-1, 100);
|
||||
var value_text = new TextView ();
|
||||
/* value_text.get_buffer ().set_text (value); */
|
||||
value_text.hexpand = true;
|
||||
value_text.get_style_context ().add_class ("contacts-entry");
|
||||
value_text.show ();
|
||||
sw.add (value_text);
|
||||
sw.show ();
|
||||
|
||||
value_text.get_buffer ().changed.connect (() => { this.dirty = true; });
|
||||
|
||||
/* return value_text; */
|
||||
return sw;
|
||||
}
|
||||
|
||||
public Button create_delete_button () {
|
||||
var delete_button = new Button.from_icon_name ("user-trash-symbolic");
|
||||
delete_button.get_accessible ().set_name (_("Delete field"));
|
||||
delete_button.clicked.connect (() => removed ());
|
||||
delete_button.show ();
|
||||
|
||||
return delete_button;
|
||||
}
|
||||
}
|
76
src/editor/contacts-editor-emails-editor.vala
Normal file
76
src/editor/contacts-editor-emails-editor.vala
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 Folks;
|
||||
using Gee;
|
||||
using Gtk;
|
||||
|
||||
public class Contacts.Editor.EmailsEditor : CompositeEditor<EmailDetails, EmailFieldDetails> {
|
||||
|
||||
public override string persona_property {
|
||||
get { return "email-addresses"; }
|
||||
}
|
||||
|
||||
public EmailsEditor (EmailDetails? details = null) {
|
||||
if (details != null) {
|
||||
var email_fields = Contact.sort_fields<EmailFieldDetails>(details.email_addresses);
|
||||
foreach (var email_field_detail in email_fields)
|
||||
this.child_editors.add (new EmailEditor (this, email_field_detail));
|
||||
} else {
|
||||
// No emails were passed on => make a single personal email address
|
||||
this.child_editors.add (new EmailEditor (this, null, "PERSONAL"));
|
||||
}
|
||||
}
|
||||
|
||||
public override async void save (EmailDetails email_details) throws PropertyError {
|
||||
yield email_details.change_email_addresses (aggregate_children ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals with a single email address field.
|
||||
*/
|
||||
public class EmailEditor : Object, CompositeEditorChild<EmailFieldDetails> {
|
||||
private TypeCombo type_combo;
|
||||
private Entry email_entry;
|
||||
private Button delete_button;
|
||||
|
||||
public EmailEditor (EmailsEditor parent, EmailFieldDetails? details = null, string? type = null) {
|
||||
this.type_combo = parent.create_type_combo (TypeSet.email, details);
|
||||
this.email_entry = parent.create_entry ();
|
||||
this.email_entry.placeholder_text = _("Add email");
|
||||
this.delete_button = parent.create_delete_button ();
|
||||
|
||||
if (details != null)
|
||||
this.email_entry.text = details.value;
|
||||
if (type != null)
|
||||
this.type_combo.set_to (type);
|
||||
}
|
||||
|
||||
public int attach_to_grid (Grid container_grid, int row) {
|
||||
container_grid.attach (this.type_combo, 0, row);
|
||||
container_grid.attach (this.email_entry, 1, row);
|
||||
container_grid.attach (this.delete_button, 2, row);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public EmailFieldDetails create_details () {
|
||||
// XXX parameters
|
||||
return new EmailFieldDetails (this.email_entry.text, null);
|
||||
}
|
||||
}
|
||||
}
|
52
src/editor/contacts-editor-full-name-editor.vala
Normal file
52
src/editor/contacts-editor-full-name-editor.vala
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 Folks;
|
||||
using Gee;
|
||||
using Gtk;
|
||||
|
||||
public class Contacts.Editor.FullNameEditor : DetailsEditor<NameDetails> {
|
||||
|
||||
private Entry name_entry;
|
||||
|
||||
public override string persona_property {
|
||||
get { return "full-name"; }
|
||||
}
|
||||
|
||||
public FullNameEditor (Contact? contact = null, NameDetails? details = null) {
|
||||
this.name_entry = create_entry ();
|
||||
this.name_entry.valign = Align.CENTER;
|
||||
this.name_entry.placeholder_text = _("Add name");
|
||||
if (contact != null)
|
||||
this.name_entry.text = contact.display_name;
|
||||
}
|
||||
|
||||
public override int attach_to_grid (Grid container_grid, int row) {
|
||||
container_grid.attach (this.name_entry, 1, row, 2, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override async void save (NameDetails name_details) throws PropertyError {
|
||||
yield name_details.change_full_name (this.name_entry.text);
|
||||
}
|
||||
|
||||
public override Value create_value () {
|
||||
Value v = Value (typeof (string));
|
||||
v.set_string (this.name_entry.text);
|
||||
return v;
|
||||
}
|
||||
}
|
57
src/editor/contacts-editor-nickname-editor.vala
Normal file
57
src/editor/contacts-editor-nickname-editor.vala
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 Folks;
|
||||
using Gee;
|
||||
using Gtk;
|
||||
|
||||
public class Contacts.Editor.NicknameEditor : DetailsEditor<NameDetails> {
|
||||
private Label label;
|
||||
private Entry nickname_entry;
|
||||
private Button delete_button;
|
||||
|
||||
public override string persona_property {
|
||||
get { return "nickname"; }
|
||||
}
|
||||
|
||||
public NicknameEditor (NameDetails? details = null) {
|
||||
this.label = create_label (_("Nickname"));
|
||||
this.nickname_entry = create_entry ();
|
||||
this.delete_button = create_delete_button ();
|
||||
|
||||
if (details != null)
|
||||
this.nickname_entry.text = details.nickname;
|
||||
}
|
||||
|
||||
public override int attach_to_grid (Grid container_grid, int row) {
|
||||
container_grid.attach (this.label, 0, row);
|
||||
container_grid.attach (this.nickname_entry, 1, row);
|
||||
container_grid.attach (this.delete_button, 2, row);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public override async void save (NameDetails name_details) throws PropertyError {
|
||||
yield name_details.change_nickname (this.nickname_entry.text);
|
||||
}
|
||||
|
||||
public override Value create_value () {
|
||||
var result = Value (typeof (string));
|
||||
result.set_string (nickname_entry.text);
|
||||
return result;
|
||||
}
|
||||
}
|
78
src/editor/contacts-editor-notes-editor.vala
Normal file
78
src/editor/contacts-editor-notes-editor.vala
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 Folks;
|
||||
using Gee;
|
||||
using Gtk;
|
||||
|
||||
/**
|
||||
* Deals with multiple "Notes"
|
||||
*/
|
||||
public class Contacts.Editor.NotesEditor : CompositeEditor<NoteDetails, NoteFieldDetails> {
|
||||
|
||||
public override string persona_property {
|
||||
get { return "notes"; }
|
||||
}
|
||||
|
||||
public NotesEditor (NoteDetails? details = null) {
|
||||
if (details != null) {
|
||||
foreach (var note_field_detail in details.notes)
|
||||
this.child_editors.add (new NoteEditor (this, note_field_detail));
|
||||
} else {
|
||||
// No notes were passed on => make a single blank editor
|
||||
this.child_editors.add (new NoteEditor (this));
|
||||
}
|
||||
}
|
||||
|
||||
public override async void save (NoteDetails note_details) throws PropertyError {
|
||||
yield note_details.change_notes (aggregate_children ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals with a single "Notes" field.
|
||||
*/
|
||||
public class NoteEditor : Object, CompositeEditorChild<NoteFieldDetails> {
|
||||
private Label label;
|
||||
private ScrolledWindow note_textview;
|
||||
private Button delete_button;
|
||||
|
||||
public NoteEditor (NotesEditor parent, NoteFieldDetails? details = null) {
|
||||
this.label = parent.create_label (_("Note"));
|
||||
this.note_textview = parent.create_textview ();
|
||||
this.delete_button = parent.create_delete_button ();
|
||||
|
||||
// XXX TODO
|
||||
/* if (details != null) */
|
||||
/* this.nickname_entry.text = details.nickname; */
|
||||
}
|
||||
|
||||
public int attach_to_grid (Grid container_grid, int row) {
|
||||
container_grid.attach (this.label, 0, row);
|
||||
container_grid.attach (this.note_textview, 1, row);
|
||||
container_grid.attach (this.delete_button, 2, row);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public NoteFieldDetails create_details () {
|
||||
// XXX parameters
|
||||
// XXX scrolledwindow
|
||||
return new NoteFieldDetails ("test niels", null);
|
||||
/* return new NoteFieldDetails (this.note_textview.buffer.text, null); */
|
||||
}
|
||||
}
|
||||
}
|
73
src/editor/contacts-editor-phones-editor.vala
Normal file
73
src/editor/contacts-editor-phones-editor.vala
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 Folks;
|
||||
using Gee;
|
||||
using Gtk;
|
||||
|
||||
public class Contacts.Editor.PhonesEditor : CompositeEditor<PhoneDetails, PhoneFieldDetails> {
|
||||
|
||||
public override string persona_property {
|
||||
get { return "phone-numbers"; }
|
||||
}
|
||||
|
||||
public PhonesEditor (PhoneDetails? details = null) {
|
||||
if (details != null) {
|
||||
var phone_fields = Contact.sort_fields<PhoneFieldDetails>(details.phone_numbers);
|
||||
foreach (var phone_nr_detail in phone_fields)
|
||||
this.child_editors.add (new PhoneEditor (this, phone_nr_detail));
|
||||
} else {
|
||||
// No phones were passed on => make a single cell phone number
|
||||
this.child_editors.add (new PhoneEditor (this, null, "CELL"));
|
||||
}
|
||||
}
|
||||
|
||||
public override async void save (PhoneDetails phone_details) throws PropertyError {
|
||||
yield phone_details.change_phone_numbers (aggregate_children ());
|
||||
}
|
||||
|
||||
public class PhoneEditor : Object, CompositeEditorChild<PhoneFieldDetails> {
|
||||
private TypeCombo type_combo;
|
||||
private Entry phone_entry;
|
||||
private Button delete_button;
|
||||
|
||||
public PhoneEditor (PhonesEditor parent, PhoneFieldDetails? details = null, string? type = null) {
|
||||
this.type_combo = parent.create_type_combo (TypeSet.phone, details);
|
||||
this.phone_entry = parent.create_entry ();
|
||||
this.phone_entry.placeholder_text = _("Add number");
|
||||
this.delete_button = parent.create_delete_button ();
|
||||
|
||||
if (details != null)
|
||||
this.phone_entry.text = details.value;
|
||||
if (type != null)
|
||||
this.type_combo.set_to (type);
|
||||
}
|
||||
|
||||
public int attach_to_grid (Grid container_grid, int row) {
|
||||
container_grid.attach (this.type_combo, 0, row);
|
||||
container_grid.attach (this.phone_entry, 1, row);
|
||||
container_grid.attach (this.delete_button, 2, row);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public PhoneFieldDetails create_details () {
|
||||
// XXX parameters
|
||||
return new PhoneFieldDetails (this.phone_entry.text, null);
|
||||
}
|
||||
}
|
||||
}
|
71
src/editor/contacts-editor-urls-editor.vala
Normal file
71
src/editor/contacts-editor-urls-editor.vala
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 Folks;
|
||||
using Gee;
|
||||
using Gtk;
|
||||
|
||||
public class Contacts.Editor.UrlsEditor : CompositeEditor<UrlDetails, UrlFieldDetails> {
|
||||
|
||||
public override string persona_property {
|
||||
get { return "urls"; }
|
||||
}
|
||||
|
||||
public UrlsEditor (UrlDetails? details = null) {
|
||||
if (details != null) {
|
||||
/* var url_fields = Contact.sort_fields<UrlFieldDetails>(details.urls); */
|
||||
/* foreach (var url_field_detail in url_fields) */
|
||||
foreach (var url_field_detail in details.urls)
|
||||
this.child_editors.add (new UrlEditor (this, url_field_detail));
|
||||
} else {
|
||||
// No urls were passed on => make a single blank editor
|
||||
this.child_editors.add (new UrlEditor (this));
|
||||
}
|
||||
}
|
||||
|
||||
public override async void save (UrlDetails url_details) throws PropertyError {
|
||||
yield url_details.change_urls (aggregate_children ());
|
||||
}
|
||||
|
||||
public class UrlEditor : Object, CompositeEditorChild<UrlFieldDetails> {
|
||||
private Label label;
|
||||
private Entry url_entry;
|
||||
private Button delete_button;
|
||||
|
||||
public UrlEditor (UrlsEditor parent, UrlFieldDetails? details = null) {
|
||||
this.label = parent.create_label (_("Website"));
|
||||
this.url_entry = parent.create_entry ();
|
||||
this.delete_button = parent.create_delete_button ();
|
||||
|
||||
if (details != null)
|
||||
this.url_entry.text = details.value;
|
||||
}
|
||||
|
||||
public int attach_to_grid (Grid container_grid, int row) {
|
||||
container_grid.attach (this.label, 0, row);
|
||||
container_grid.attach (this.url_entry, 1, row);
|
||||
container_grid.attach (this.delete_button, 2, row);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public UrlFieldDetails create_details () {
|
||||
// XXX parameters
|
||||
return new UrlFieldDetails (this.url_entry.text, null);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue