/* * Copyright (C) 2011 Alexander Larsson * Copyright (C) 2021 Niels De Graef * * 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 . */ using Folks; const int PROFILE_SIZE = 128; /** * The ContactPage is the right pane. It consists of 3 possible pages: * a page if nothing is selected, a ContactSheet to view contact information, * and a ContactEditor to edit contact information. */ [GtkTemplate (ui = "/org/gnome/Contacts/ui/contacts-contact-pane.ui")] public class Contacts.ContactPane : Adw.Bin { private unowned Store store; private Contact? contact = null; [GtkChild] private unowned Gtk.Stack stack; [GtkChild] private unowned Adw.Clamp contact_sheet_clamp; private unowned ContactSheet? sheet = null; [GtkChild] private unowned Gtk.Box contact_editor_box; private unowned ContactEditor? editor = null; public bool on_edit_mode = false; private LinkSuggestionGrid? suggestion_grid = null; public signal void contacts_linked (LinkOperation operation); public ContactPane (MainWindow main_window, Store contacts_store) { this.store = contacts_store; } public void add_suggestion (Individual individual, Individual other) { unowned var parent_overlay = this.get_parent () as Gtk.Overlay; remove_suggestion_grid (); this.suggestion_grid = new LinkSuggestionGrid (other); this.suggestion_grid.valign = Gtk.Align.END; parent_overlay.add_overlay (this.suggestion_grid); this.suggestion_grid.suggestion_accepted.connect (() => { var to_link = new Gee.LinkedList (); to_link.add (individual); to_link.add (other); var operation = new LinkOperation (this.store, to_link); this.contacts_linked (operation); remove_suggestion_grid (); }); this.suggestion_grid.suggestion_rejected.connect (() => { /* TODO: Add undo */ store.add_no_suggest_link (individual, other); remove_suggestion_grid (); }); } public void show_contact (Individual? individual) { if (individual == null) { this.contact = null; remove_contact_sheet (); this.stack.set_visible_child_name ("none-selected-page"); return; } if (this.contact == null || this.contact.individual != individual) { this.contact = new Contact.for_individual (individual); } show_contact_sheet (this.contact); } private void show_contact_sheet (Contact contact) requires (this.contact != null) { remove_contact_sheet (); var contacts_sheet = new ContactSheet (contact); contacts_sheet.hexpand = true; this.sheet = contacts_sheet; this.contact_sheet_clamp.set_child (this.sheet); this.stack.set_visible_child_name ("contact-sheet-page"); // Show potential link suggestions only if it's an existing contact if (contact.individual != null) { var matches = this.store.aggregator.get_potential_matches (contact.individual, MatchResult.HIGH); foreach (var i in matches.keys) { if (i != null && this.store.suggest_link_to (contact.individual, i)) { add_suggestion (contact.individual, i); break; } } } } private void remove_contact_sheet () { if (this.sheet == null) return; // Remove the suggestion grid that goes along with it. remove_suggestion_grid (); this.contact_sheet_clamp.set_child (null); this.sheet = null; } private void create_contact_editor () requires (this.contact != null) { remove_contact_editor (); var contact_editor = new ContactEditor (this.contact); contact_editor.hexpand = true; this.editor = contact_editor; this.contact_editor_box.append (this.editor); } private void remove_contact_editor () { if (this.editor == null) return; this.contact_editor_box.remove (this.editor); this.editor = null; } public void stop_editing (bool cancel = false) requires (this.on_edit_mode) { this.on_edit_mode = false; remove_contact_editor (); if (cancel) { if (this.contact != null) { this.stack.set_visible_child_name ("contact-sheet-page"); } else { this.stack.set_visible_child_name ("none-selected-page"); } } else { // Save changes if editing wasn't canceled apply_changes.begin (this.contact); } } private async void apply_changes (Contact contact) { // TODO: block changes to contact show_contact_sheet (contact); // Wait that the store gets quiescent if it isn't already if (!this.store.aggregator.is_quiescent) { ulong signal_id; SourceFunc callback = apply_changes.callback; signal_id = this.store.quiescent.connect (() => { callback (); }); yield; disconnect (signal_id); } try { // The new individual. Even when editing an exisiting contact, it might // be a different Individual than before, so make sure to adjust our // selected contact afterwards unowned var individual = yield contact.apply_changes (this.store.aggregator.primary_store); debug ("Applied changes resulted in individual (%s)", (individual != null)? individual.id : "null"); if (individual != null) { var pos = yield this.store.find_individual_for_id (individual.id); if (pos != Gtk.INVALID_LIST_POSITION) this.store.selection.selected = pos; } } catch (Error err) { warning ("Couldn't save changes: %s", err.message); show_contact (null); // XXX do something better here } } public void edit_contact () requires (this.contact != null) { if (this.on_edit_mode) return; this.on_edit_mode = true; create_contact_editor (); this.stack.set_visible_child_name ("contact-editor-page"); } public void new_contact () { this.contact = new Contact.empty (); if (this.on_edit_mode) return; this.on_edit_mode = true; create_contact_editor (); this.stack.set_visible_child_name ("contact-editor-page"); } private void remove_suggestion_grid () { if (this.suggestion_grid == null) return; unowned var parent_overlay = this.get_parent () as Gtk.Overlay; parent_overlay.remove_overlay (suggestion_grid); this.suggestion_grid = null; } }