Add first run dialog
This commit is contained in:
parent
0db26f1d31
commit
b0b19005ab
10 changed files with 347 additions and 58 deletions
|
@ -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])
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
213
src/contacts-setup-window.vala
Normal file
213
src/contacts-setup-window.vala
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
10
src/org.gnome.Contacts.gschema.xml.in
Normal file
10
src/org.gnome.Contacts.gschema.xml.in
Normal 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>
|
|
@ -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")]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue