diff options
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | src/Makefile.am | 15 | ||||
-rw-r--r-- | src/contacts-app.vala | 42 | ||||
-rw-r--r-- | src/contacts-esd-setup.c | 45 | ||||
-rw-r--r-- | src/contacts-esd-setup.h | 5 | ||||
-rw-r--r-- | src/contacts-list-pane.vala | 54 | ||||
-rw-r--r-- | src/contacts-setup-window.vala | 213 | ||||
-rw-r--r-- | src/contacts-store.vala | 13 | ||||
-rw-r--r-- | src/org.gnome.Contacts.gschema.xml.in | 10 | ||||
-rw-r--r-- | vapi/custom.vapi | 6 |
10 files changed, 347 insertions, 58 deletions
diff --git a/configure.ac b/configure.ac index 315386b..06faef1 100644 --- a/configure.ac +++ b/configure.ac @@ -20,6 +20,8 @@ AC_PROG_CC AM_PROG_VALAC([0.14.0]) AC_PROG_INSTALL +GLIB_GSETTINGS + # i18n stuff IT_PROG_INTLTOOL([0.40]) diff --git a/src/Makefile.am b/src/Makefile.am index 18b1e9f..d6ff529 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,10 +35,19 @@ vala_sources = \ contacts-avatar-menu.vala \ contacts-contact-frame.vala \ contacts-revealer.vala \ + contacts-setup-window.vala \ contacts-window.vala \ main.vala \ $(NULL) +gsettingsschema_in_files = org.gnome.Contacts.gschema.xml.in +gsettings_SCHEMAS = $(gsettingsschema_in_files:.xml.in=.xml) +.PRECIOUS: $(gsettings_SCHEMAS) + +@INTLTOOL_XML_NOMERGE_RULE@ + +@GSETTINGS_RULES@ + contact-resources.c: contacts.gresource.xml app-menu.ui $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $(srcdir)/contacts.gresource.xml \ --target=$@ --sourcedir=$(srcdir) --c-name contacts --generate-source @@ -52,9 +61,13 @@ gnome_contacts_SOURCES = \ gnome_contacts_LDADD = $(CONTACTS_LIBS) -lm -CLEANFILES = $(vala_sources:.vala=.c) *.vapi *.stamp +CLEANFILES = \ + $(vala_sources:.vala=.c) \ + $(gsettings_SCHEMAS) \ + *.vapi *.stamp EXTRA_DIST = \ gtk-notification.h \ + $(gsettingsschema_in_files) \ contacts-esd-setup.h \ $(NULL) diff --git a/src/contacts-app.vala b/src/contacts-app.vala index 2a7bbad..e0588e8 100644 --- a/src/contacts-app.vala +++ b/src/contacts-app.vala @@ -20,6 +20,7 @@ using Gtk; using Folks; public class Contacts.App : Gtk.Application { + public GLib.Settings settings; public Contacts.Window window; public static App app; public Store contacts_store; @@ -304,8 +305,46 @@ public class Contacts.App : Gtk.Application { base.startup (); } + private void show_setup () { + avoid_goa_workaround = true; + var setup = new SetupWindow (); + setup.set_application (this); + setup.destroy.connect ( () => { + avoid_goa_workaround = false; + setup.destroy (); + if (setup.succeeded) + this.activate (); + }); + setup.show (); + } + public override void activate () { if (window == null) { + if (!settings.get_boolean ("did-initial-setup")) { + if (contacts_store.is_prepared) + show_setup (); + else { + hold (); + ulong id = 0; + uint id2 = 0; + id = contacts_store.prepared.connect (() => { + show_setup (); + contacts_store.disconnect (id); + Source.remove (id2); + release (); + }); + // Wait at most 0.5 seconds to show the window + id2 = Timeout.add (500, () => { + show_setup (); + contacts_store.disconnect (id); + release (); + return false; + }); + } + + return; + } + create_window (); // We delay the initial show a tiny bit so most contacts are loaded when we show @@ -381,7 +420,7 @@ public class Contacts.App : Gtk.Application { }); overlay.add_overlay (notification); } - + public override int command_line (ApplicationCommandLine command_line) { var args = command_line.get_arguments (); unowned string[] _args = args; @@ -413,5 +452,6 @@ public class Contacts.App : Gtk.Application { public App () { Object (application_id: "org.gnome.Contacts", flags: ApplicationFlags.HANDLES_COMMAND_LINE); this.app = this; + settings = new GLib.Settings ("org.gnome.Contacts"); } } diff --git a/src/contacts-esd-setup.c b/src/contacts-esd-setup.c index 9efeae6..858a654 100644 --- a/src/contacts-esd-setup.c +++ b/src/contacts-esd-setup.c @@ -33,7 +33,8 @@ static gboolean created_local = FALSE; static GMainLoop *goa_loop; static GoaClient *goa_client; static GHashTable *accounts; -static ESourceList *contacts_source_list; +ESourceList *contacts_source_list; +gboolean contacts_avoid_goa_workaround = FALSE; /* This whole file is a gigantic hack that copies and pastes stuff from * evolution to create evolution-data-server addressbooks as needed. @@ -176,7 +177,7 @@ ensure_local_addressbook (void) client = e_book_client_new_system (NULL); if (client != NULL) { - contacts_eds_local_store = g_strdup (e_source_peek_uid (e_client_get_source (client))); + contacts_eds_local_store = g_strdup (e_source_peek_uid (e_client_get_source (E_CLIENT (client)))); g_object_unref (client); return TRUE; } @@ -345,7 +346,7 @@ online_accounts_account_added_cb (GoaClient *goa_client, // a while to let a running evo instance // create the account, this is a lame // fix for the race condition - if (goa_loop == NULL) { + if (!contacts_avoid_goa_workaround) { struct SyncData *data = g_new (struct SyncData, 1); data->uid = g_strdup (evo_id); data->goa_object = g_object_ref (goa_object); @@ -559,6 +560,7 @@ void contacts_ensure_eds_accounts (void) created_local = ensure_local_addressbook (); goa_loop = g_main_loop_new (NULL, TRUE); + contacts_avoid_goa_workaround = TRUE; online_accounts_connect (); @@ -567,11 +569,48 @@ void contacts_ensure_eds_accounts (void) g_main_loop_unref (goa_loop); goa_loop = NULL; + contacts_avoid_goa_workaround = FALSE; contacts_source_list = NULL; e_book_get_addressbooks (&contacts_source_list, NULL); } +gboolean contacts_has_goa_account (void) +{ + GSList *list_a; + + list_a = e_source_list_peek_groups (contacts_source_list); + while (list_a != NULL) { + ESourceGroup *source_group; + GSList *list_b; + + source_group = E_SOURCE_GROUP (list_a->data); + list_a = g_slist_next (list_a); + + list_b = e_source_group_peek_sources (source_group); + + while (list_b != NULL) { + ESource *source; + const gchar *property; + const gchar *uid; + GList *match; + + source = E_SOURCE (list_b->data); + list_b = g_slist_next (list_b); + + uid = e_source_peek_uid (source); + property = e_source_get_property (source, GOA_KEY); + + if (property == NULL) + continue; + + return TRUE; + } + } + + return FALSE; +} + /* This is an enourmous hack to find google eds contacts that are in the "My Contacts" system group. */ diff --git a/src/contacts-esd-setup.h b/src/contacts-esd-setup.h index 83ed38c..8b4f8e4 100644 --- a/src/contacts-esd-setup.h +++ b/src/contacts-esd-setup.h @@ -1,6 +1,11 @@ +#include <libedataserver/e-source-list.h> + void contacts_ensure_eds_accounts (void); extern char *contacts_eds_local_store; const char *contacts_lookup_esource_name_by_uid (const char *uid); const char *contacts_lookup_esource_name_by_uid_for_contact (const char *uid); gboolean contacts_esource_uid_is_google (const char *uid); char *eds_personal_google_group_name (void); +gboolean contacts_has_goa_account (void); +extern ESourceList *contacts_source_list; +extern gboolean contacts_avoid_goa_workaround; diff --git a/src/contacts-list-pane.vala b/src/contacts-list-pane.vala index 0fa2249..4620602 100644 --- a/src/contacts-list-pane.vala +++ b/src/contacts-list-pane.vala @@ -25,8 +25,6 @@ public class Contacts.ListPane : Frame { private ViewWidget list; public Entry filter_entry; private uint filter_entry_changed_id; - private ulong non_empty_id; - private EventBox empty_box; private bool ignore_selection_change; private Revealer search_revealer; private bool search_visible; @@ -156,64 +154,14 @@ public class Contacts.ListPane : Frame { list.show_all (); scrolled.set_no_show_all (true); - empty_box = new EventBox (); - empty_box.set_hexpand (false); - empty_box.set_vexpand (true); - empty_box.set_halign (Align.FILL); - Gdk.RGBA white = {1, 1, 1, 1}; - empty_box.override_background_color (StateFlags.NORMAL, white); - - var empty_grid = new Grid (); - empty_grid.set_row_spacing (8); - empty_grid.set_orientation (Orientation.VERTICAL); - empty_grid.set_valign (Align.CENTER); - - var image = new Image.from_icon_name ("avatar-default-symbolic", IconSize.DIALOG); - image.get_style_context ().add_class ("dim-label"); - empty_grid.add (image); - - var label = new Label (_("Connect to an account,\nimport or add contacts")); - label.xalign = 0.5f; - label.set_hexpand (true); - label.set_halign (Align.CENTER); - empty_grid.add (label); - - var button = new Button.with_label (_("Online Accounts")); - button.set_halign (Align.CENTER); - empty_grid.add (button); - button.clicked.connect ( (button) => { - try { - Process.spawn_command_line_async ("gnome-control-center online-accounts"); - } - catch (Error e) { - // TODO: Show error dialog - } - }); - - empty_box.add (empty_grid); - empty_box.show_all (); - empty_box.set_no_show_all (true); - grid.add (search_revealer); grid.add (scrolled); - grid.add (empty_box); this.show_all (); search_revealer.set_no_show_all (true); search_revealer.hide (); - if (contacts_store.is_empty ()) { - empty_box.show (); - non_empty_id = contacts_store.added.connect ( (c) => { - empty_box.hide (); - scrolled.show (); - contacts_store.disconnect (non_empty_id); - non_empty_id = 0; - }); - } else { - scrolled.show (); - } - + scrolled.show (); } public void select_contact (Contact contact, bool ignore_change = false) { diff --git a/src/contacts-setup-window.vala b/src/contacts-setup-window.vala new file mode 100644 index 0000000..9f494cc --- /dev/null +++ b/src/contacts-setup-window.vala @@ -0,0 +1,213 @@ +/* -*- Mode: vala; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */ +/* + * 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; + +public class Contacts.SetupWindow : Gtk.Window { + public bool succeeded; + private ulong source_list_changed_id; + public Label title_label; + public Grid content_grid; + ToolButton select_button; + ListStore list_store; + TreeView tree_view; + + public void update_content () { + foreach (var w in content_grid.get_children ()) + w.destroy (); + + var l = new Label (""); + l.set_markup ("<b>%s</b>".printf (_("Welcome to Contacts!"))); + content_grid.add (l); + + Button goa_button; + + if (has_goa_account ()) { + select_button.show (); + + tree_view = new TreeView (); + var store = new ListStore (2, typeof (string), typeof (Folks.PersonaStore)); + list_store = store; + tree_view.set_model (store); + tree_view.set_headers_visible (false); + tree_view.get_selection ().set_mode (SelectionMode.BROWSE); + + var column = new Gtk.TreeViewColumn (); + tree_view.append_column (column); + + var renderer = new Gtk.CellRendererText (); + column.pack_start (renderer, false); + column.add_attribute (renderer, "text", 0); + + var scrolled = new ScrolledWindow(null, null); + scrolled.set_size_request (340, 240); + scrolled.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC); + scrolled.set_vexpand (true); + scrolled.set_shadow_type (ShadowType.IN); + scrolled.add (tree_view); + + content_grid.add (scrolled); + + TreeIter iter; + foreach (var persona_store in Contact.get_eds_address_books ()) { + var name = Contact.format_persona_store_name (persona_store); + store.append (out iter); + store.set (iter, 0, name, 1, persona_store); + if (persona_store == App.app.contacts_store.aggregator.primary_store) { + tree_view.get_selection ().select_iter (iter); + } + } + + goa_button = new Button.with_label (_("Online Account Settings")); + content_grid.add (goa_button); + + } else { + select_button.hide (); + l = new Label (_("Setup an online account or use a local address book")); + content_grid.add (l); + + goa_button = new Button.with_label (_("Online Accounts")); + content_grid.add (goa_button); + + var b = new Button.with_label (_("Use Local Address Book")); + content_grid.add (b); + + b.clicked.connect ( () => { + var source = eds_source_list.peek_source_by_uid (eds_local_store); + select_source (source); + }); + } + + goa_button.clicked.connect ( (button) => { + try { + update_content (); + Process.spawn_command_line_async ("gnome-control-center online-accounts"); + } + catch (Error e) { + // TODO: Show error dialog + } + }); + + content_grid.show_all (); + } + + private void select_source (E.Source source) { + try { + E.BookClient.set_default_source (source); + } catch { + warning ("Failed to set address book"); + } + succeeded = true; + App.app.settings.set_boolean ("did-initial-setup", true); + destroy (); + } + + + public SetupWindow () { + var grid = new Grid (); + this.add (grid); + this.set_title (_("Contacts Setup")); + this.set_default_size (640, 480); + + this.hide_titlebar_when_maximized = true; + + var toolbar = new Toolbar (); + toolbar.set_icon_size (IconSize.MENU); + toolbar.get_style_context ().add_class (STYLE_CLASS_MENUBAR); + toolbar.set_vexpand (false); + toolbar.set_hexpand (true); + grid.attach (toolbar, 0, 0, 1, 1); + + var cancel_button = new ToolButton (null, _("Cancel")); + cancel_button.is_important = true; + toolbar.add (cancel_button); + cancel_button.clicked.connect ( (button) => { + this.destroy (); + }); + + var item = new ToolItem (); + title_label = new Label (""); + title_label.set_markup ("<b>%s</b>".printf (_("Contacts Setup"))); + title_label.set_no_show_all (true); + item.add (title_label); + item.set_expand (true); + toolbar.add (item); + + select_button = new ToolButton (null, _("Select")); + select_button.is_important = true; + select_button.set_no_show_all (true); + toolbar.add (select_button); + select_button.clicked.connect ( (button) => { + PersonaStore selected_store; + TreeIter iter; + + if (tree_view.get_selection() .get_selected (null, out iter)) { + list_store.get (iter, 1, out selected_store); + + var e_store = selected_store as Edsf.PersonaStore; + select_source (e_store.source); + } + }); + + var frame = new Frame (null); + frame.get_style_context ().add_class ("contacts-content"); + + var box = new EventBox (); + box.set_hexpand (true); + box.set_vexpand (true); + box.get_style_context ().add_class ("contacts-main-view"); + box.get_style_context ().add_class ("view"); + + frame.add (box); + grid.attach (frame, 0, 1, 1, 1); + + content_grid = new Grid (); + content_grid.set_orientation (Orientation.VERTICAL); + content_grid.set_halign (Align.CENTER); + content_grid.set_row_spacing (8); + box.add (content_grid); + + update_content (); + + source_list_changed_id = eds_source_list.changed.connect ( () => { + update_content (); + }); + + grid.show_all (); + } + + public override void destroy () { + if (source_list_changed_id != 0) { + eds_source_list.disconnect (source_list_changed_id); + source_list_changed_id = 0; + } + base.destroy (); + } + + public override bool window_state_event (Gdk.EventWindowState e) { + base.window_state_event (e); + + if ((e.new_window_state & Gdk.WindowState.MAXIMIZED) != 0) + title_label.show (); + else + title_label.hide (); + + return false; + } +} diff --git a/src/contacts-store.vala b/src/contacts-store.vala index 45b8d83..f66ae3b 100644 --- a/src/contacts-store.vala +++ b/src/contacts-store.vala @@ -26,6 +26,7 @@ public class Contacts.Store : GLib.Object { public signal void added (Contact c); public signal void removed (Contact c); public signal void quiescent (); + public signal void prepared (); public IndividualAggregator aggregator { get; private set; } public BackendStore backend_store { get; private set; } @@ -45,6 +46,10 @@ public class Contacts.Store : GLib.Object { get { return this.aggregator.is_quiescent; } } + public bool is_prepared { + get { return this.aggregator.is_prepared; } + } + public void refresh () { foreach (var c in contacts) { c.queue_changed (true); @@ -144,6 +149,14 @@ public class Contacts.Store : GLib.Object { return false; }); }); + + aggregator.notify["is-prepared"].connect ( (obj, pspec) => { + Idle.add( () => { + this.prepared (); + return false; + }); + }); + aggregator.individuals_changed_detailed.connect ( (changes) => { // Note: Apparently the current implementation doesn't necessarily pick // up unlinked individual as replacements. diff --git a/src/org.gnome.Contacts.gschema.xml.in b/src/org.gnome.Contacts.gschema.xml.in new file mode 100644 index 0000000..3987072 --- /dev/null +++ b/src/org.gnome.Contacts.gschema.xml.in @@ -0,0 +1,10 @@ +<schemalist> + <schema id="org.gnome.Contacts" path="/org/gnome/Contacts/" gettext-domain="gnome-contacts"> + <key name="did-initial-setup" type="b"> + <default>false</default> + <_summary>First-time setup done.</_summary> + <_description>Set to true when the user ran the first-time setup wizard.</_description> + </key> + </schema> + +</schemalist> diff --git a/vapi/custom.vapi b/vapi/custom.vapi index eace183..d947f9b 100644 --- a/vapi/custom.vapi +++ b/vapi/custom.vapi @@ -37,6 +37,12 @@ namespace Contacts { public static bool esource_uid_is_google (string uid); [CCode (cname = "eds_personal_google_group_name")] public static unowned string? eds_personal_google_group_name (); + [CCode (cname = "contacts_has_goa_account")] + public static bool has_goa_account (); + [CCode (cname = "contacts_source_list")] + public static E.SourceList eds_source_list; + [CCode (cname = "contacts_avoid_goa_workaround")] + public static bool avoid_goa_workaround; } [CCode (cprefix = "Gtk", lower_case_cprefix = "gtk_", cheader_filename = "gtk-notification.h")] |