/* -*- Mode: vala; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */ /* * Copyright (C) 2011 Alexander Larsson * * 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 Gee; using Gtk; using Folks; [GtkTemplate (ui = "/org/gnome/contacts/ui/contacts-window.ui")] public class Contacts.Window : Gtk.ApplicationWindow { [GtkChild] private Grid content_grid; [GtkChild] private Grid loading_box; [GtkChild] private SizeGroup left_pane_size_group; [GtkChild] private HeaderBar left_toolbar; [GtkChild] private HeaderBar right_toolbar; [GtkChild] private Overlay overlay; [GtkChild] private ContactPane contact_pane; [GtkChild] private Button add_button; [GtkChild] private ToggleButton select_button; [GtkChild] private Button edit_button; [GtkChild] private Button cancel_button; [GtkChild] private Button done_button; [GtkChild] private Stack view_switcher; [GtkChild] private Box content_header_bar; [GtkChild] private HeaderBar setup_header_bar; [GtkChild] private Button setup_done_button; [GtkChild] private Button setup_cancel_button; [GtkChild] private AccountsList setup_accounts_list; private ListPane list_pane; private string left_title { get { return left_toolbar.get_title (); } set { left_toolbar.set_title (value); } } private string right_title { get { return right_toolbar.get_title (); } set { right_toolbar.set_title (value); } } private bool new_contact_mode = false; public Store store { get; construct set; } public bool selection_mode { get; set; } public bool edit_mode { get; set; } public Window (Gtk.Application app, Store contacts_store) { Object (application: app, store: contacts_store); debug ("everyone creation: finalized already!!!"); contact_pane.store = contacts_store; /* stablishing constraints */ this.bind_property ("selection-mode", right_toolbar, "show-close-button", BindingFlags.DEFAULT | BindingFlags.INVERT_BOOLEAN); this.bind_property ("selection-mode", add_button, "visible", BindingFlags.DEFAULT | BindingFlags.INVERT_BOOLEAN); this.bind_property ("selection-mode", edit_button, "visible", BindingFlags.DEFAULT | BindingFlags.INVERT_BOOLEAN); this.bind_property ("edit-mode", edit_button, "visible", BindingFlags.DEFAULT | BindingFlags.INVERT_BOOLEAN); this.bind_property ("edit-mode", done_button, "visible", BindingFlags.DEFAULT); this.bind_property ("edit-mode", cancel_button, "visible", BindingFlags.DEFAULT); this.bind_property ("edit-mode", add_button, "visible", BindingFlags.DEFAULT | BindingFlags.INVERT_BOOLEAN); this.bind_property ("edit-mode", select_button, "visible", BindingFlags.DEFAULT | BindingFlags.INVERT_BOOLEAN); this.bind_property ("edit-mode", right_toolbar, "show-close-button", BindingFlags.DEFAULT | BindingFlags.INVERT_BOOLEAN); if ((app as App).settings.get_boolean ("did-initial-setup")) { view_switcher.visible_child_name = "content-view"; set_titlebar (content_header_bar); } else { var change_book_action = app.lookup_action ("change_book") as GLib.SimpleAction; if (change_book_action != null) { change_book_action.set_enabled (false); } store.eds_persona_store_changed.connect ( () => { setup_accounts_list.update_contents (false); }); ulong id2 = 0; id2 = setup_accounts_list.account_selected.connect (() => { setup_done_button.set_sensitive (true); setup_accounts_list.disconnect (id2); }); view_switcher.visible_child_name = "setup-view"; set_titlebar (setup_header_bar); setup_accounts_list.update_contents (false); setup_done_button.clicked.connect (() => { view_switcher.visible_child_name = "content-view"; set_titlebar (content_header_bar); var e_store = setup_accounts_list.selected_store as Edsf.PersonaStore; eds_source_registry.set_default_address_book (e_store.source); (app as App).settings.set_boolean ("did-initial-setup", true); if (change_book_action != null) { change_book_action.set_enabled (true); } }); setup_cancel_button.clicked.connect (() => { app.quit (); }); } init_content_widgets (); } public void set_list_pane () { /* FIXME: if no contact is loaded per backend, I must place a sign * saying "import your contacts/add online account" */ if (list_pane != null) return; list_pane = new ListPane (store); list_pane.selection_changed.connect (list_pane_selection_changed_cb); list_pane.link_contacts.connect (list_pane_link_contacts_cb); list_pane.delete_contacts.connect (list_pane_delete_contacts_cb); list_pane.contacts_marked.connect ((nr_contacts) => { if (nr_contacts == 0) { left_title = _("Select"); } else { left_title = ngettext ("%d Selected", "%d Selected", nr_contacts).printf (nr_contacts); } }); left_pane_size_group.add_widget (list_pane); left_pane_size_group.remove_widget (loading_box); loading_box.destroy (); content_grid.attach (list_pane, 0, 0, 1, 1); if (contact_pane.contact != null) list_pane.select_contact (contact_pane.contact); list_pane.show (); } public void activate_selection_mode (bool active) { if (active) { selection_mode = true; left_toolbar.get_style_context ().add_class ("selection-mode"); right_toolbar.get_style_context ().add_class ("selection-mode"); left_toolbar.set_title (_("Select")); list_pane.show_selection (); } else { selection_mode = false; left_toolbar.get_style_context ().remove_class ("selection-mode"); right_toolbar.get_style_context ().remove_class ("selection-mode"); left_toolbar.set_title (_("All Contacts")); list_pane.hide_selection (); /* could be no contact selected whatsoever */ if (contact_pane.contact == null) edit_button.hide (); } } public void enter_edit_mode () { if (contact_pane.contact == null) return; edit_mode = true; var name = contact_pane.contact.display_name; right_title = _("Editing %s").printf (name); left_toolbar.get_style_context ().add_class ("selection-mode"); right_toolbar.get_style_context ().add_class ("selection-mode"); contact_pane.set_edit_mode (true); } public void leave_edit_mode (bool drop_changes = false) { edit_mode = false; left_toolbar.get_style_context ().remove_class ("selection-mode"); right_toolbar.get_style_context ().remove_class ("selection-mode"); if (new_contact_mode) { done_button.label = _("Done"); if (drop_changes) { contact_pane.set_edit_mode (false, drop_changes); } else { contact_pane.create_contact (); } new_contact_mode = false; } else { contact_pane.set_edit_mode (false, drop_changes); } if (contact_pane.contact != null) { right_title = contact_pane.contact.display_name; } else { right_title = ""; edit_button.hide (); } } public void add_notification (Widget notification) { overlay.add_overlay (notification); } public void set_shown_contact (Contact? c) { /* FIXME: ask the user to leave edit-mode and act accordingly */ if (contact_pane.on_edit_mode) { leave_edit_mode (); } contact_pane.show_contact (c, false); if (list_pane != null) list_pane.select_contact (c); /* clearing right_toolbar */ if (c != null) right_title = c.display_name; else right_title = ""; edit_button.visible = (c != null) && !select_button.active; } [GtkCallback] public void new_contact () { /* FIXME: eventually ContactPane will become just a skeleton and * this call will go through to ContactEditor */ edit_mode = true; new_contact_mode = true; right_title = _("New Contact"); left_toolbar.get_style_context ().add_class ("selection-mode"); right_toolbar.get_style_context ().add_class ("selection-mode"); done_button.label = _("Add"); contact_pane.new_contact (); } public void show_search (string query) { list_pane.filter_entry.set_text (query); } /* internal API */ void init_content_widgets () { string layout_desc; string[] tokens; layout_desc = Gtk.Settings.get_default ().gtk_decoration_layout; tokens = layout_desc.split (":", 2); if (tokens != null) { right_toolbar.decoration_layout = ":%s".printf (tokens[1]); left_toolbar.decoration_layout = tokens[0]; } select_button.toggled.connect (() => { activate_selection_mode (select_button.active); }); edit_button.clicked.connect (() => { enter_edit_mode (); }); done_button.clicked.connect (() => { leave_edit_mode (); }); cancel_button.clicked.connect (() => { leave_edit_mode (true); }); } [GtkCallback] bool key_press_event_cb (Gdk.EventKey event) { if ((event.keyval == Gdk.keyval_from_name ("q")) && ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0)) { // Clear the contacts so any changed information is stored contact_pane.show_contact (null); destroy (); } else if (((event.keyval == Gdk.Key.s) || (event.keyval == Gdk.Key.f)) && ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0)) { Utils.grab_entry_focus_no_select (list_pane.filter_entry); } else if (event.length >= 1 && Gdk.keyval_to_unicode (event.keyval) != 0 && (event.state & Gdk.ModifierType.CONTROL_MASK) == 0 && (event.state & Gdk.ModifierType.MOD1_MASK) == 0 && (event.keyval != Gdk.Key.Escape) && (event.keyval != Gdk.Key.Tab) && (event.keyval != Gdk.Key.BackSpace) ) { Utils.grab_entry_focus_no_select (list_pane.filter_entry); propagate_key_event (event); } return false; } [GtkCallback] bool delete_event_cb (Gdk.EventAny event) { // Clear the contacts so any changed information is stored contact_pane.show_contact (null); return false; } void list_pane_selection_changed_cb (Contact? new_selection) { set_shown_contact (new_selection); } void list_pane_link_contacts_cb (LinkedList contact_list) { /* getting out of selection mode */ set_shown_contact (null); select_button.set_active (false); LinkOperation2 operation = null; link_contacts_list.begin (contact_list, (obj, result) => { operation = link_contacts_list.end (result); }); var notification = new Gd.Notification (); notification.timeout = 5; var g = new Grid (); g.set_column_spacing (8); notification.add (g); string msg = ngettext ("%d contacts linked", "%d contacts linked", contact_list.size).printf (contact_list.size); var b = new Button.with_mnemonic (_("_Undo")); g.add (new Label (msg)); g.add (b); notification.show_all (); add_notification (notification); /* signal handlers */ b.clicked.connect ( () => { /* here, we will unlink the thing in question */ operation.undo.begin (); notification.dismiss (); }); } void list_pane_delete_contacts_cb (LinkedList contact_list) { /* getting out of selection mode */ set_shown_contact (null); select_button.set_active (false); var notification = new Gd.Notification (); notification.timeout = 5; var g = new Grid (); g.set_column_spacing (8); notification.add (g); string msg = ngettext ("%d contact deleted", "%d contacts deleted", contact_list.size).printf (contact_list.size); var b = new Button.with_mnemonic (_("_Undo")); g.add (new Label (msg)); g.add (b); notification.show_all (); add_notification (notification); /* signal handlers */ bool really_delete = true; notification.dismissed.connect ( () => { if (really_delete) { foreach (var c in contact_list) { c.remove_personas.begin (); } } }); b.clicked.connect ( () => { really_delete = false; notification.dismiss (); foreach (var c in contact_list) { c.show (); } set_shown_contact (contact_list.last ()); }); } [GtkCallback] void contact_pane_delete_contact_cb (Contact contact) { /* unsetting edit-mode */ set_shown_contact (null); select_button.set_active (false); var notification = new Gd.Notification (); notification.timeout = 5; var g = new Grid (); g.set_column_spacing (8); notification.add (g); var label = new Label (_("Contact deleted: \"%s\"").printf (contact.display_name)); label.set_max_width_chars (45); label.set_ellipsize (Pango.EllipsizeMode.END); var b = new Button.with_mnemonic (_("_Undo")); g.add (label); g.add (b); bool really_delete = true; notification.show_all (); notification.dismissed.connect ( () => { if (really_delete) contact.remove_personas.begin ( () => { contact.show (); }); }); b.clicked.connect ( () => { really_delete = false; notification.dismiss (); contact.show (); set_shown_contact (contact); }); add_notification (notification); } [GtkCallback] void contact_pane_contacts_linked_cb (string? main_contact, string linked_contact, LinkOperation operation) { var notification = new Gd.Notification (); notification.timeout = 5; var g = new Grid (); g.set_column_spacing (8); notification.add (g); string msg; if (main_contact != null) msg = _("%s linked to %s").printf (main_contact, linked_contact); else msg = _("%s linked to the contact").printf (linked_contact); var b = new Button.with_mnemonic (_("_Undo")); g.add (new Label (msg)); g.add (b); notification.show_all (); b.clicked.connect ( () => { notification.dismiss (); operation.undo.begin (); }); add_notification (notification); } }