Add first run dialog

This commit is contained in:
Alexander Larsson 2012-01-27 16:21:21 +01:00
parent 0db26f1d31
commit b0b19005ab
10 changed files with 347 additions and 58 deletions

View file

@ -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])

View file

@ -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)

View file

@ -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");
}
}

View file

@ -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. */

View file

@ -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;

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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.

View file

@ -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>

View file

@ -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")]