3f58a4ab2d
In this case it is fairly obvious that new_active and old_active will both be set before use. There is no need for setting old_active to FALSE. cppcheck warned about this.
3571 lines
122 KiB
C
3571 lines
122 KiB
C
/*
|
|
* Copyright (C) 2009 Intel Corporation
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) version 3.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA
|
|
*/
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <gtk/gtk.h>
|
|
#include <glib/gi18n.h>
|
|
#include <gio/gio.h>
|
|
#include <dbus/dbus-glib.h>
|
|
#include <dbus/dbus-glib-bindings.h>
|
|
#include <gio/gdesktopappinfo.h>
|
|
#include <gdk/gdk.h>
|
|
|
|
#include "syncevo-server.h"
|
|
#include "syncevo-session.h"
|
|
|
|
/* for return value definitions */
|
|
/* TODO: would be nice to have a non-synthesis-dependent API but for now it's like this... */
|
|
#include <synthesis/syerror.h>
|
|
|
|
#include "config.h"
|
|
#include "sync-ui-config.h"
|
|
#include "sync-ui.h"
|
|
#include "sync-config-widget.h"
|
|
|
|
#ifdef USE_MOBLIN_UX
|
|
#include "mux-frame.h"
|
|
#endif
|
|
|
|
static gboolean support_canceling = FALSE;
|
|
#define REPORTS_PER_CALL 10
|
|
|
|
#define SYNC_UI_ICON_SIZE 48
|
|
|
|
#define STRING_VARIANT_HASHTABLE (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE))
|
|
|
|
enum {
|
|
PAGE_MAIN,
|
|
PAGE_SETTINGS,
|
|
PAGE_EMERGENCY,
|
|
};
|
|
|
|
typedef enum bluetooth_type {
|
|
SYNC_BLUETOOTH_NONE,
|
|
SYNC_BLUETOOTH_GNOME,
|
|
SYNC_BLUETOOTH_MOBLIN
|
|
} bluetooth_type;
|
|
|
|
typedef enum app_state {
|
|
SYNC_UI_STATE_CURRENT_STATE,
|
|
SYNC_UI_STATE_GETTING_SERVER,
|
|
SYNC_UI_STATE_NO_SERVER,
|
|
SYNC_UI_STATE_SERVER_OK,
|
|
SYNC_UI_STATE_SERVER_FAILURE,
|
|
SYNC_UI_STATE_SYNCING,
|
|
} app_state;
|
|
|
|
typedef enum ui_operation {
|
|
OP_SYNC, /* use sync mode from config */
|
|
OP_SYNC_SLOW,
|
|
OP_SYNC_REFRESH_FROM_CLIENT,
|
|
OP_SYNC_REFRESH_FROM_SERVER,
|
|
OP_SAVE,
|
|
OP_RESTORE,
|
|
} ui_operation;
|
|
|
|
typedef struct operation_data {
|
|
app_data *data;
|
|
ui_operation operation;
|
|
gboolean started;
|
|
const char *dir; /* for OP_RESTORE */
|
|
} operation_data;
|
|
|
|
struct _app_data {
|
|
GtkWidget *sync_win;
|
|
|
|
GtkWidget *services_win; /* will be NULL when USE_MOBLIN_UX is set*/
|
|
GtkWidget *emergency_win; /* will be NULL when USE_MOBLIN_UX is set*/
|
|
#ifdef USE_MOBLIN_UX
|
|
GtkWidget *notebook; /* only in use with USE_MOBLIN_UX */
|
|
GtkWidget *back_btn; /* only in use with USE_MOBLIN_UX */
|
|
#endif
|
|
GtkWidget *settings_btn; /* only in use with USE_MOBLIN_UX */
|
|
|
|
guint settings_id;
|
|
|
|
GtkWidget *service_box;
|
|
GtkWidget *info_bar;
|
|
GtkWidget *no_connection_box;
|
|
GtkWidget *main_frame;
|
|
GtkWidget *log_frame;
|
|
GtkWidget *server_icon_box;
|
|
|
|
GtkWidget *offline_label;
|
|
GtkWidget *progress;
|
|
GtkWidget *sync_status_label;
|
|
GtkWidget *spinner_image;
|
|
GtkWidget *sync_btn;
|
|
GtkWidget *change_service_btn;
|
|
GtkWidget *emergency_btn;
|
|
|
|
GtkWidget *server_label;
|
|
GtkWidget *autosync_box;
|
|
GtkWidget *autosync_toggle;
|
|
GtkWidget *last_synced_label;
|
|
GtkWidget *sources_box;
|
|
|
|
GtkWidget *new_service_btn;
|
|
GtkWidget *new_device_btn;
|
|
GtkWidget *services_box;
|
|
GtkWidget *devices_box;
|
|
GtkWidget *scrolled_window;
|
|
GtkWidget *expanded_config;
|
|
GtkWidget *settings_close_btn;
|
|
|
|
GtkWidget *emergency_label;
|
|
GtkWidget *emergency_expander;
|
|
GtkWidget *emergency_source_table;
|
|
GtkWidget *refresh_from_server_btn_label;
|
|
GtkWidget *refresh_from_client_btn_label;
|
|
GtkWidget *emergency_backup_table;
|
|
GtkWidget *emergency_close_btn;
|
|
|
|
GtkWidget *password_dialog_entry;
|
|
char *password_dialog_id;
|
|
|
|
gboolean forced_emergency;
|
|
GHashTable *emergency_sources;
|
|
guint backup_count;
|
|
|
|
gboolean online;
|
|
|
|
gboolean syncing;
|
|
gboolean synced_this_session;
|
|
int last_sync;
|
|
guint last_sync_src_id;
|
|
|
|
ui_operation current_operation;
|
|
server_config *current_service;
|
|
app_state current_state;
|
|
guint service_list_updates_left;
|
|
gboolean open_current; /* should the service list open the current
|
|
service when it populates next time*/
|
|
char *config_id_to_open;
|
|
|
|
SyncevoServer *server;
|
|
|
|
SyncevoSession *running_session; /* session that is currently active */
|
|
|
|
bluetooth_type bluetooth_wizard;
|
|
};
|
|
|
|
static void set_sync_progress (app_data *data, float progress, char *status);
|
|
static void set_app_state (app_data *data, app_state state);
|
|
static void show_main_view (app_data *data);
|
|
static void update_emergency_view (app_data *data);
|
|
static void update_emergency_expander (app_data *data);
|
|
static void show_emergency_view (app_data *data);
|
|
static void show_services_list (app_data *data, const char *config_id_to_open);
|
|
static void update_services_list (app_data *data);
|
|
static void update_service_ui (app_data *data);
|
|
static void setup_new_service_clicked (GtkButton *btn, app_data *data);
|
|
static gboolean source_config_update_widget (source_config *source);
|
|
static void get_presence_cb (SyncevoServer *server, char *status, char **transport,
|
|
GError *error, app_data *data);
|
|
static void get_reports_cb (SyncevoServer *server, SyncevoReports *reports,
|
|
GError *error, app_data *data);
|
|
static void start_session_cb (SyncevoServer *server, char *path,
|
|
GError *error, operation_data *op_data);
|
|
static void get_config_for_main_win_cb (SyncevoServer *server, SyncevoConfig *config,
|
|
GError *error, app_data *data);
|
|
|
|
|
|
void
|
|
show_error_dialog (GtkWidget *widget, const char* message)
|
|
{
|
|
GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (widget));
|
|
|
|
GtkWidget *w;
|
|
w = gtk_message_dialog_new (window,
|
|
GTK_DIALOG_MODAL,
|
|
GTK_MESSAGE_ERROR,
|
|
GTK_BUTTONS_OK,
|
|
"%s",
|
|
message);
|
|
gtk_dialog_run (GTK_DIALOG (w));
|
|
gtk_widget_destroy (w);
|
|
}
|
|
|
|
|
|
static void
|
|
remove_child (GtkWidget *widget, GtkContainer *container)
|
|
{
|
|
gtk_container_remove (container, widget);
|
|
}
|
|
|
|
static void
|
|
change_service_clicked_cb (GtkButton *btn, app_data *data)
|
|
{
|
|
/* data->open_current = TRUE; */
|
|
show_services_list (data, NULL);
|
|
}
|
|
|
|
static void
|
|
emergency_clicked_cb (GtkButton *btn, app_data *data)
|
|
{
|
|
show_emergency_view (data);
|
|
}
|
|
|
|
|
|
char*
|
|
get_pretty_source_name (const char *source_name)
|
|
{
|
|
/* TRANSLATORS: There have been name changes to keep things in line with
|
|
* the rest of the moblin UI. Please make sure the name you use matches
|
|
* the ones in e.g. the panels. */
|
|
if (strcmp (source_name, "addressbook") == 0) {
|
|
return g_strdup (_("Contacts"));
|
|
} else if (strcmp (source_name, "calendar") == 0) {
|
|
return g_strdup (_("Appointments"));
|
|
} else if (strcmp (source_name, "todo") == 0) {
|
|
return g_strdup (_("Tasks"));
|
|
} else if (strcmp (source_name, "memo") == 0) {
|
|
return g_strdup (_("Notes"));
|
|
} else if (strcmp (source_name, "calendar+todo") == 0) {
|
|
/* TRANSLATORS: This is a "combination source" for syncing with devices
|
|
* that combine appointments and tasks. the name should match the ones
|
|
* used for calendar and todo above */
|
|
return g_strdup (_("Appointments & Tasks"));
|
|
} else {
|
|
char *tmp;
|
|
tmp = g_strdup (source_name);
|
|
tmp[0] = g_ascii_toupper (tmp[0]);
|
|
return tmp;
|
|
}
|
|
}
|
|
|
|
char*
|
|
get_pretty_source_name_markup (const char *source_name)
|
|
{
|
|
char *plain, *markup;
|
|
|
|
plain = get_pretty_source_name (source_name);
|
|
markup = g_markup_escape_text (plain, -1);
|
|
g_free (plain);
|
|
return markup;
|
|
}
|
|
|
|
static void
|
|
reload_config (app_data *data, const char *server)
|
|
{
|
|
server_config_free (data->current_service);
|
|
data->forced_emergency = FALSE;
|
|
g_hash_table_remove_all (data->emergency_sources);
|
|
|
|
if (!server || strlen (server) == 0) {
|
|
data->current_service = NULL;
|
|
update_service_ui (data);
|
|
set_app_state (data, SYNC_UI_STATE_SERVER_OK);
|
|
} else {
|
|
data->synced_this_session = FALSE;
|
|
data->current_service = g_slice_new0 (server_config);
|
|
data->current_service->name = g_strdup (server);
|
|
set_app_state (data, SYNC_UI_STATE_GETTING_SERVER);
|
|
|
|
syncevo_server_get_config (data->server,
|
|
data->current_service->name,
|
|
FALSE,
|
|
(SyncevoServerGetConfigCb)get_config_for_main_win_cb,
|
|
data);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
abort_sync_cb (SyncevoSession *session,
|
|
GError *error,
|
|
app_data *data)
|
|
{
|
|
if (error) {
|
|
/* TODO show in UI: failed to abort sync (while syncing) */
|
|
g_error_free (error);
|
|
}
|
|
|
|
/* status change handler takes care of updating UI */
|
|
}
|
|
|
|
static void
|
|
sync_cb (SyncevoSession *session,
|
|
GError *error,
|
|
app_data *data)
|
|
{
|
|
if (error) {
|
|
/* TODO show in UI: sync failed (failed to even start) */
|
|
g_error_free (error);
|
|
g_object_unref (session);
|
|
return;
|
|
}
|
|
|
|
set_sync_progress (data, 0.0, _("Starting sync"));
|
|
/* stop updates of "last synced" */
|
|
if (data->last_sync_src_id > 0)
|
|
g_source_remove (data->last_sync_src_id);
|
|
set_app_state (data, SYNC_UI_STATE_SYNCING);
|
|
}
|
|
|
|
gboolean
|
|
show_confirmation (GtkWidget *widget, const char *message,
|
|
const char *yes, const char *no)
|
|
{
|
|
GtkWidget *w;
|
|
int ret;
|
|
|
|
w = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (widget)),
|
|
GTK_DIALOG_MODAL,
|
|
GTK_MESSAGE_QUESTION,
|
|
GTK_BUTTONS_NONE,
|
|
"%s",
|
|
message);
|
|
gtk_dialog_add_buttons (GTK_DIALOG (w),
|
|
no, GTK_RESPONSE_NO,
|
|
yes, GTK_RESPONSE_YES,
|
|
NULL);
|
|
ret = gtk_dialog_run (GTK_DIALOG (w));
|
|
gtk_widget_destroy (w);
|
|
|
|
return (ret == GTK_RESPONSE_YES);
|
|
}
|
|
|
|
static void
|
|
slow_sync (app_data *data)
|
|
{
|
|
operation_data *op_data;
|
|
char *message;
|
|
|
|
/* TRANSLATORS: slow sync confirmation dialog message. Placeholder
|
|
* is service/device name */
|
|
message = g_strdup_printf (_("Do you want to slow sync with %s?"),
|
|
data->current_service->pretty_name);
|
|
/* TRANSLATORS: slow sync confirmation dialog buttons */
|
|
if (!show_confirmation (data->sync_win, message,
|
|
_("Yes, do slow sync"), _("No, cancel sync"))) {
|
|
g_free (message);
|
|
return;
|
|
}
|
|
g_free (message);
|
|
|
|
op_data = g_slice_new (operation_data);
|
|
op_data->data = data;
|
|
op_data->operation = OP_SYNC_SLOW;
|
|
op_data->started = FALSE;
|
|
syncevo_server_start_session (data->server,
|
|
data->current_service->name,
|
|
(SyncevoServerStartSessionCb)start_session_cb,
|
|
op_data);
|
|
|
|
show_main_view (data);
|
|
}
|
|
|
|
static void
|
|
slow_sync_clicked_cb (GtkButton *btn, app_data *data)
|
|
{
|
|
slow_sync (data);
|
|
}
|
|
|
|
|
|
static void
|
|
refresh_from_server_clicked_cb (GtkButton *btn, app_data *data)
|
|
{
|
|
operation_data *op_data;
|
|
char *message;
|
|
|
|
/* TRANSLATORS: confirmation dialog for "refresh from peer". Placeholder
|
|
* is service/device name */
|
|
message = g_strdup_printf (_("Do you want to delete all local data and replace it with "
|
|
"data from %s? This is not usually advised."),
|
|
data->current_service->pretty_name);
|
|
/* TRANSLATORS: "refresh from peer" confirmation dialog buttons */
|
|
if (!show_confirmation (data->sync_win, message,
|
|
_("Yes, delete and replace"), _("No"))) {
|
|
g_free (message);
|
|
return;
|
|
}
|
|
g_free (message);
|
|
|
|
op_data = g_slice_new (operation_data);
|
|
op_data->data = data;
|
|
op_data->operation = peer_is_client (data->current_service->config) ?
|
|
OP_SYNC_REFRESH_FROM_CLIENT :
|
|
OP_SYNC_REFRESH_FROM_SERVER;
|
|
op_data->started = FALSE;
|
|
syncevo_server_start_session (data->server,
|
|
data->current_service->name,
|
|
(SyncevoServerStartSessionCb)start_session_cb,
|
|
op_data);
|
|
|
|
show_main_view (data);
|
|
}
|
|
|
|
static void
|
|
refresh_from_client_clicked_cb (GtkButton *btn, app_data *data)
|
|
{
|
|
operation_data *op_data;
|
|
char *message;
|
|
|
|
/* TRANSLATORS: confirmation dialog for "refresh from local side". Placeholder
|
|
* is service/device name */
|
|
message = g_strdup_printf (_("Do you want to delete all data in %s and replace it with "
|
|
"your local data? This is not usually advised."),
|
|
data->current_service->pretty_name);
|
|
/* TRANSLATORS: "refresh from local side" confirmation dialog buttons */
|
|
if (!show_confirmation (data->sync_win, message,
|
|
_("Yes, delete and replace"), _("No"))) {
|
|
g_free (message);
|
|
return;
|
|
}
|
|
g_free (message);
|
|
|
|
op_data = g_slice_new (operation_data);
|
|
op_data->data = data;
|
|
op_data->operation = peer_is_client (data->current_service->config) ?
|
|
OP_SYNC_REFRESH_FROM_SERVER :
|
|
OP_SYNC_REFRESH_FROM_CLIENT;
|
|
op_data->started = FALSE;
|
|
syncevo_server_start_session (data->server,
|
|
data->current_service->name,
|
|
(SyncevoServerStartSessionCb)start_session_cb,
|
|
op_data);
|
|
|
|
show_main_view (data);
|
|
}
|
|
|
|
static void
|
|
start_sync (app_data *data)
|
|
{
|
|
operation_data *op_data;
|
|
|
|
if (data->syncing) {
|
|
syncevo_session_abort (data->running_session,
|
|
(SyncevoSessionGenericCb)abort_sync_cb,
|
|
data);
|
|
set_sync_progress (data, -1.0, _("Trying to cancel sync"));
|
|
} else {
|
|
|
|
op_data = g_slice_new (operation_data);
|
|
op_data->data = data;
|
|
op_data->operation = OP_SYNC;
|
|
op_data->started = FALSE;
|
|
syncevo_server_start_session (data->server,
|
|
data->current_service->name,
|
|
(SyncevoServerStartSessionCb)start_session_cb,
|
|
op_data);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
sync_clicked_cb (GtkButton *btn, app_data *data)
|
|
{
|
|
g_return_if_fail (data->current_service);
|
|
|
|
start_sync (data);
|
|
}
|
|
|
|
#define DAY 60 * 60 * 24
|
|
#define HALF_DAY 60 * 60 * 12
|
|
#define HOUR 60 * 60
|
|
#define HALF_HOUR 60 * 30
|
|
#define MINUTE 60
|
|
#define HALF_MINUTE 30
|
|
|
|
static gboolean
|
|
refresh_last_synced_label (app_data *data)
|
|
{
|
|
GTimeVal val;
|
|
glong diff;
|
|
char *msg;
|
|
int delay;
|
|
|
|
g_get_current_time (&val);
|
|
diff = val.tv_sec - data->last_sync;
|
|
|
|
if (!data->current_service) {
|
|
msg = g_strdup (_("No service or device selected"));
|
|
delay = -1;
|
|
} else if (data->last_sync <= 0) {
|
|
msg = g_strdup (data->current_service->pretty_name); /* we don't know */
|
|
delay = -1;
|
|
} else if (diff < HALF_MINUTE) {
|
|
/* TRANSLATORS: This is the title on main view. Placeholder is
|
|
* the service name. Example: "Google - synced just now" */
|
|
msg = g_strdup_printf (_("%s - synced just now"),
|
|
data->current_service->pretty_name);
|
|
delay = 10;
|
|
} else if (diff < MINUTE + HALF_MINUTE) {
|
|
msg = g_strdup_printf (_("%s - synced a minute ago"),
|
|
data->current_service->pretty_name);
|
|
delay = MINUTE;
|
|
} else if (diff < HOUR) {
|
|
msg = g_strdup_printf (_("%s - synced %ld minutes ago"),
|
|
data->current_service->pretty_name,
|
|
(diff + HALF_MINUTE) / MINUTE);
|
|
delay = MINUTE;
|
|
} else if (diff < HOUR + HALF_HOUR) {
|
|
msg = g_strdup_printf (_("%s - synced an hour ago"),
|
|
data->current_service->pretty_name);
|
|
delay = HOUR;
|
|
} else if (diff < DAY) {
|
|
msg = g_strdup_printf (_("%s - synced %ld hours ago"),
|
|
data->current_service->pretty_name,
|
|
(diff + HALF_HOUR) / (HOUR));
|
|
delay = HOUR;
|
|
} else if (diff < DAY + HALF_DAY) {
|
|
msg = g_strdup_printf (_("%s - synced a day ago"),
|
|
data->current_service->pretty_name);
|
|
delay = HOUR;
|
|
} else {
|
|
msg = g_strdup_printf (_("%s - synced %ld days ago"),
|
|
data->current_service->pretty_name,
|
|
(diff + HALF_DAY) / (DAY));
|
|
delay = HOUR;
|
|
}
|
|
|
|
gtk_label_set_text (GTK_LABEL (data->server_label), msg);
|
|
g_free (msg);
|
|
|
|
if (data->last_sync_src_id > 0)
|
|
g_source_remove (data->last_sync_src_id);
|
|
if (delay > 0)
|
|
data->last_sync_src_id = g_timeout_add_seconds (delay, (GSourceFunc)refresh_last_synced_label, data);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
set_sync_progress (app_data *data, float progress, char *status)
|
|
{
|
|
if (progress >= 0)
|
|
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (data->progress), progress);
|
|
if (status)
|
|
gtk_progress_bar_set_text (GTK_PROGRESS_BAR (data->progress), status);
|
|
}
|
|
|
|
static void
|
|
set_info_bar (GtkWidget *widget,
|
|
GtkMessageType type,
|
|
SyncErrorResponse response_id,
|
|
const char *message)
|
|
{
|
|
GtkWidget *container, *label;
|
|
GtkInfoBar *bar = GTK_INFO_BAR (widget);
|
|
|
|
if (!message) {
|
|
gtk_widget_hide (widget);
|
|
return;
|
|
}
|
|
|
|
container = gtk_info_bar_get_action_area (bar);
|
|
gtk_container_foreach (GTK_CONTAINER (container),
|
|
(GtkCallback)remove_child,
|
|
container);
|
|
switch (response_id) {
|
|
case SYNC_ERROR_RESPONSE_SYNC:
|
|
/* TRANSLATORS: Action button in info bar in main view. Shown with e.g.
|
|
* "You've just restored a backup. The changes have not been "
|
|
* "synced with %s yet"
|
|
* Please make this text multi-line if your text is longer
|
|
* than ~25 chars (example: "My very long\nbutton title") */
|
|
gtk_info_bar_add_button (bar, _("Sync now"), response_id);
|
|
break;
|
|
case SYNC_ERROR_RESPONSE_EMERGENCY:
|
|
/* TRANSLATORS: Action button in info bar in main view. Shown with e.g.
|
|
* "A normal sync is not possible at this time..." message.
|
|
* "Other options" will open Emergency view
|
|
* Please make this text multi-line if your text is longer
|
|
* than ~25 chars (example: "My very long\nbutton title") */
|
|
gtk_info_bar_add_button (bar, _("Slow sync"), SYNC_ERROR_RESPONSE_EMERGENCY_SLOW_SYNC);
|
|
gtk_info_bar_add_button (bar, _("Other options..."), response_id);
|
|
break;
|
|
case SYNC_ERROR_RESPONSE_SETTINGS_SELECT:
|
|
/* TRANSLATORS: Action button in info bar in main view. Shown e.g.
|
|
* when no service is selected. Will open configuration view
|
|
* Please make this text multi-line if your text is longer
|
|
* than ~25 chars (example: "My very long\nbutton title") */
|
|
gtk_info_bar_add_button (bar, _("Select sync service"), response_id);
|
|
break;
|
|
case SYNC_ERROR_RESPONSE_SETTINGS_OPEN:
|
|
/* TRANSLATORS: Action button in info bar in main view. Shown e.g.
|
|
* login to service fails. Will open configuration view for this service
|
|
* (e.g. "Edit service\nsettings")
|
|
* Please make this text multi-line if your text is longer
|
|
* than ~25 chars (example: "My very long\nbutton title") */
|
|
gtk_info_bar_add_button (bar, _("Edit service settings"), response_id);
|
|
break;
|
|
case SYNC_ERROR_RESPONSE_NONE:
|
|
break;
|
|
default:
|
|
g_warn_if_reached ();
|
|
}
|
|
|
|
gtk_info_bar_set_message_type (bar, type);
|
|
container = gtk_info_bar_get_content_area (bar);
|
|
gtk_container_foreach (GTK_CONTAINER (container),
|
|
(GtkCallback)remove_child,
|
|
container);
|
|
|
|
label = gtk_label_new (message);
|
|
gtk_widget_set_name (label, "info_label");
|
|
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
|
|
gtk_widget_set_size_request (label, 250, -1);
|
|
gtk_box_pack_start (GTK_BOX (container), label, FALSE, FALSE, 8);
|
|
gtk_widget_show (label);
|
|
gtk_widget_show (widget);
|
|
}
|
|
|
|
static void
|
|
set_app_state (app_data *data, app_state state)
|
|
{
|
|
if (state != SYNC_UI_STATE_CURRENT_STATE)
|
|
data->current_state = state;
|
|
|
|
switch (data->current_state) {
|
|
case SYNC_UI_STATE_GETTING_SERVER:
|
|
gtk_widget_hide (data->service_box);
|
|
gtk_widget_hide (data->info_bar);
|
|
gtk_label_set_text (GTK_LABEL (data->sync_status_label), "");
|
|
refresh_last_synced_label (data);
|
|
|
|
gtk_widget_set_sensitive (data->main_frame, TRUE);
|
|
gtk_widget_set_sensitive (data->sync_btn, FALSE);
|
|
gtk_widget_set_sensitive (data->change_service_btn, FALSE);
|
|
gtk_widget_set_sensitive (data->emergency_btn, FALSE);
|
|
|
|
if (data->settings_btn)
|
|
gtk_widget_set_sensitive (data->settings_btn, FALSE);
|
|
|
|
break;
|
|
case SYNC_UI_STATE_SERVER_FAILURE:
|
|
gtk_widget_hide (data->service_box);
|
|
gtk_widget_hide (data->autosync_box);
|
|
gtk_widget_hide (data->progress);
|
|
refresh_last_synced_label (data);
|
|
|
|
/* info bar content should be set earlier */
|
|
gtk_widget_show (data->info_bar);
|
|
|
|
gtk_label_set_text (GTK_LABEL (data->sync_status_label), "");
|
|
|
|
gtk_widget_set_sensitive (data->main_frame, FALSE);
|
|
gtk_widget_set_sensitive (data->sync_btn, FALSE);
|
|
gtk_widget_set_sensitive (data->emergency_btn, FALSE);
|
|
gtk_widget_set_sensitive (data->change_service_btn, FALSE);
|
|
|
|
if (data->settings_btn)
|
|
gtk_widget_set_sensitive (data->settings_btn, FALSE);
|
|
|
|
break;
|
|
case SYNC_UI_STATE_SERVER_OK:
|
|
if (data->online) {
|
|
gtk_widget_hide (data->no_connection_box);
|
|
} else {
|
|
gtk_widget_show (data->no_connection_box);
|
|
}
|
|
|
|
/* TRANSLATORS: These are for the button in main view, right side.
|
|
Keep line length below ~20 characters, use two lines if needed */
|
|
gtk_button_set_label (GTK_BUTTON (data->sync_btn),
|
|
_("Sync now"));
|
|
|
|
if (!data->current_service) {
|
|
gtk_widget_hide (data->service_box);
|
|
gtk_widget_hide (data->autosync_box);
|
|
gtk_widget_hide (data->progress);
|
|
set_info_bar (data->info_bar,
|
|
GTK_MESSAGE_INFO, SYNC_ERROR_RESPONSE_SETTINGS_SELECT,
|
|
_("You haven't selected a sync service or device yet. "
|
|
"Sync services let you synchronize your data "
|
|
"between your netbook and a web service. You can "
|
|
"also sync directly with some devices."));
|
|
refresh_last_synced_label (data);
|
|
|
|
gtk_label_set_text (GTK_LABEL (data->sync_status_label), "");
|
|
|
|
gtk_widget_set_sensitive (data->sync_btn, FALSE);
|
|
gtk_widget_set_sensitive (data->emergency_btn, FALSE);
|
|
gtk_window_set_focus (GTK_WINDOW (data->sync_win),
|
|
data->change_service_btn);
|
|
} else {
|
|
gtk_widget_hide (data->info_bar);
|
|
gtk_widget_show (data->service_box);
|
|
gtk_widget_show (data->autosync_box);
|
|
gtk_widget_set_sensitive (data->sync_btn, data->online);
|
|
gtk_widget_set_sensitive (data->emergency_btn, TRUE);
|
|
if (data->synced_this_session && data->current_operation != OP_RESTORE) {
|
|
gtk_button_set_label (GTK_BUTTON (data->sync_btn),
|
|
_("Sync again"));
|
|
} else {
|
|
gtk_widget_hide (data->progress);
|
|
}
|
|
gtk_window_set_focus (GTK_WINDOW (data->sync_win), data->sync_btn);
|
|
}
|
|
|
|
gtk_widget_set_sensitive (data->main_frame, TRUE);
|
|
gtk_widget_set_sensitive (data->change_service_btn, TRUE);
|
|
|
|
if (data->settings_btn)
|
|
gtk_widget_set_sensitive (data->settings_btn, TRUE);
|
|
|
|
data->syncing = FALSE;
|
|
break;
|
|
|
|
case SYNC_UI_STATE_SYNCING:
|
|
/* we have a active session, and a session is running
|
|
(the running session may or may not be ours) */
|
|
gtk_widget_show (data->progress);
|
|
if (data->current_operation == OP_RESTORE) {
|
|
gtk_label_set_text (GTK_LABEL (data->sync_status_label), _("Restoring"));
|
|
} else {
|
|
gtk_label_set_text (GTK_LABEL (data->sync_status_label), _("Syncing"));
|
|
}
|
|
gtk_widget_set_sensitive (data->main_frame, FALSE);
|
|
gtk_widget_set_sensitive (data->change_service_btn, FALSE);
|
|
gtk_widget_set_sensitive (data->emergency_btn, FALSE);
|
|
|
|
if (data->settings_btn)
|
|
gtk_widget_set_sensitive (data->settings_btn, FALSE);
|
|
|
|
gtk_widget_set_sensitive (data->sync_btn,
|
|
support_canceling && data->current_operation != OP_RESTORE);
|
|
if (support_canceling && support_canceling && data->current_operation != OP_RESTORE) {
|
|
/* TRANSLATORS: This is for the button in main view, right side.
|
|
Keep line length below ~20 characters, use two lines if needed */
|
|
gtk_button_set_label (GTK_BUTTON (data->sync_btn), _("Cancel sync"));
|
|
}
|
|
|
|
data->syncing = TRUE;
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
#ifdef USE_MOBLIN_UX
|
|
|
|
static GtkWidget*
|
|
switch_dummy_to_mux_frame (GtkWidget *dummy)
|
|
{
|
|
GtkWidget *frame, *parent;
|
|
const char *title;
|
|
|
|
g_assert (GTK_IS_BIN (dummy));
|
|
|
|
frame = mux_frame_new ();
|
|
gtk_widget_set_name (frame, gtk_widget_get_name (dummy));
|
|
title = gtk_frame_get_label (GTK_FRAME(dummy));
|
|
if (title && strlen (title) > 0)
|
|
gtk_frame_set_label (GTK_FRAME (frame), title);
|
|
|
|
parent = gtk_widget_get_parent (dummy);
|
|
g_assert (GTK_IS_BOX (parent));
|
|
|
|
gtk_widget_reparent (gtk_bin_get_child (GTK_BIN (dummy)), frame);
|
|
gtk_container_remove (GTK_CONTAINER (parent), dummy);
|
|
|
|
gtk_box_pack_start (GTK_BOX (parent), frame, TRUE, TRUE, 0);
|
|
gtk_widget_show (frame);
|
|
return frame;
|
|
}
|
|
|
|
static void
|
|
set_page (app_data *data, int page)
|
|
{
|
|
int current = gtk_notebook_get_current_page (GTK_NOTEBOOK (data->notebook));
|
|
|
|
if (page != current) {
|
|
gtk_notebook_set_current_page (GTK_NOTEBOOK (data->notebook),
|
|
page);
|
|
if (page != PAGE_MAIN) {
|
|
gtk_widget_show (data->back_btn);
|
|
} else {
|
|
gtk_widget_hide (data->back_btn);
|
|
}
|
|
|
|
/* make sure the toggle is correct */
|
|
g_signal_handler_block (data->settings_btn, data->settings_id);
|
|
if (page == PAGE_SETTINGS) {
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->settings_btn),
|
|
TRUE);
|
|
} else if (current == PAGE_SETTINGS) {
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->settings_btn),
|
|
FALSE);
|
|
}
|
|
g_signal_handler_unblock (data->settings_btn, data->settings_id);
|
|
}
|
|
|
|
gtk_window_present (GTK_WINDOW (data->sync_win));
|
|
}
|
|
|
|
|
|
static void
|
|
settings_toggled (GtkToggleButton *button, app_data *data)
|
|
{
|
|
int page = gtk_notebook_get_current_page (GTK_NOTEBOOK (data->notebook));
|
|
|
|
if (page == PAGE_SETTINGS) {
|
|
show_main_view (data);
|
|
} else {
|
|
show_services_list (data, NULL);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
key_press_cb (GtkWidget *widget,
|
|
GdkEventKey *event,
|
|
app_data *data)
|
|
{
|
|
int page = gtk_notebook_get_current_page (GTK_NOTEBOOK (data->notebook));
|
|
|
|
if (event->keyval == GDK_KEY_Escape && page != PAGE_MAIN) {
|
|
show_main_view (data);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* For some reason metacity sometimes won't maximize but will if asked
|
|
* another time. For the record, I'm not proud of writing this */
|
|
static gboolean
|
|
try_maximize (GtkWindow *win)
|
|
{
|
|
static int count = 0;
|
|
|
|
count++;
|
|
gtk_window_maximize (win);
|
|
|
|
return (count < 10);
|
|
}
|
|
|
|
|
|
static void
|
|
setup_windows (app_data *data,
|
|
GtkWidget *main,
|
|
GtkWidget *settings,
|
|
GtkWidget *emergency)
|
|
{
|
|
GtkWidget *tmp, *toolbar, *close_btn;
|
|
GtkToolItem *item;
|
|
|
|
g_assert (GTK_IS_WINDOW (main));
|
|
g_assert (GTK_IS_WINDOW (settings));
|
|
g_assert (GTK_IS_WINDOW (emergency));
|
|
|
|
data->sync_win = main;
|
|
data->services_win = NULL;
|
|
data->emergency_win = NULL;
|
|
|
|
/* populate the notebook with window contents */
|
|
data->notebook = gtk_notebook_new ();
|
|
gtk_widget_show (data->notebook);
|
|
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (data->notebook), FALSE);
|
|
gtk_notebook_set_show_border (GTK_NOTEBOOK (data->notebook), FALSE);
|
|
|
|
gtk_window_maximize (GTK_WINDOW (data->sync_win));
|
|
g_timeout_add (10, (GSourceFunc)try_maximize, data->sync_win);
|
|
gtk_window_set_decorated (GTK_WINDOW (data->sync_win), FALSE);
|
|
gtk_widget_set_name (data->sync_win, "meego_win");
|
|
g_signal_connect (data->sync_win, "key-press-event",
|
|
G_CALLBACK (key_press_cb), data);
|
|
|
|
tmp = g_object_ref (gtk_bin_get_child (GTK_BIN (data->sync_win)));
|
|
gtk_container_remove (GTK_CONTAINER (data->sync_win), tmp);
|
|
gtk_notebook_append_page (GTK_NOTEBOOK (data->notebook), tmp, NULL);
|
|
g_object_unref (tmp);
|
|
|
|
tmp = g_object_ref (gtk_bin_get_child (GTK_BIN (settings)));
|
|
gtk_container_remove (GTK_CONTAINER (settings), tmp);
|
|
gtk_notebook_append_page (GTK_NOTEBOOK (data->notebook), tmp, NULL);
|
|
g_object_unref (tmp);
|
|
|
|
tmp = g_object_ref (gtk_bin_get_child (GTK_BIN (emergency)));
|
|
gtk_container_remove (GTK_CONTAINER (emergency), tmp);
|
|
gtk_notebook_append_page (GTK_NOTEBOOK (data->notebook), tmp, NULL);
|
|
g_object_unref (tmp);
|
|
|
|
tmp = gtk_vbox_new (FALSE, 0);
|
|
gtk_widget_show (tmp);
|
|
gtk_container_add (GTK_CONTAINER (data->sync_win), tmp);
|
|
|
|
gtk_box_pack_end (GTK_BOX (tmp), data->notebook,
|
|
TRUE, TRUE, 0);
|
|
|
|
/* create the window toolbar */
|
|
toolbar = gtk_toolbar_new ();
|
|
gtk_widget_set_name (toolbar, "moblin-toolbar");
|
|
gtk_box_pack_start (GTK_BOX (tmp), toolbar,
|
|
FALSE, FALSE, 0);
|
|
|
|
data->back_btn = gtk_button_new_with_label (_("Back to sync"));
|
|
gtk_widget_set_name (data->back_btn, "moblin-toolbar-button");
|
|
gtk_widget_set_can_focus (data->back_btn, FALSE);
|
|
gtk_widget_set_no_show_all (data->back_btn, TRUE);
|
|
g_signal_connect_swapped (data->back_btn, "clicked",
|
|
G_CALLBACK (show_main_view), data);
|
|
item = gtk_tool_item_new ();
|
|
gtk_container_add (GTK_CONTAINER (item), data->back_btn);
|
|
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, 0);
|
|
|
|
item = gtk_tool_item_new ();
|
|
gtk_tool_item_set_expand (item, TRUE);
|
|
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, 1);
|
|
|
|
data->settings_btn = gtk_toggle_button_new ();
|
|
gtk_widget_set_can_focus (data->settings_btn, FALSE);
|
|
gtk_widget_set_name (data->settings_btn, "moblin-settings-button");
|
|
data->settings_id = g_signal_connect (data->settings_btn, "toggled",
|
|
G_CALLBACK (settings_toggled), data);
|
|
|
|
gtk_container_add (GTK_CONTAINER (data->settings_btn),
|
|
gtk_image_new_from_icon_name ("preferences-other",
|
|
GTK_ICON_SIZE_LARGE_TOOLBAR));
|
|
item = gtk_tool_item_new ();
|
|
gtk_container_add (GTK_CONTAINER (item), data->settings_btn);
|
|
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
|
|
|
|
close_btn = gtk_button_new ();
|
|
gtk_widget_set_can_focus (close_btn, FALSE);
|
|
gtk_widget_set_name (close_btn, "moblin-close-button");
|
|
g_signal_connect (close_btn, "clicked",
|
|
G_CALLBACK (gtk_main_quit), NULL);
|
|
gtk_container_add (GTK_CONTAINER (close_btn),
|
|
gtk_image_new_from_icon_name ("window-close",
|
|
GTK_ICON_SIZE_LARGE_TOOLBAR));
|
|
item = gtk_tool_item_new ();
|
|
gtk_container_add (GTK_CONTAINER (item), close_btn);
|
|
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
|
|
|
|
gtk_widget_show_all (toolbar);
|
|
|
|
/* no need for close buttons */
|
|
gtk_widget_hide (data->settings_close_btn);
|
|
gtk_widget_hide (data->emergency_close_btn);
|
|
}
|
|
|
|
static void
|
|
show_emergency_view (app_data *data)
|
|
{
|
|
update_emergency_view (data);
|
|
set_page (data, PAGE_EMERGENCY);
|
|
}
|
|
|
|
static void
|
|
show_services_list (app_data *data, const char *config_id_to_open)
|
|
{
|
|
g_free (data->config_id_to_open);
|
|
data->config_id_to_open = g_strdup (config_id_to_open);
|
|
set_page (data, PAGE_SETTINGS);
|
|
update_services_list (data);
|
|
}
|
|
|
|
static void
|
|
show_main_view (app_data *data)
|
|
{
|
|
set_page (data, PAGE_MAIN);
|
|
}
|
|
|
|
#else
|
|
|
|
/* return the placeholders themselves when not using Moblin UX */
|
|
static GtkWidget*
|
|
switch_dummy_to_mux_frame (GtkWidget *dummy) {
|
|
return dummy;
|
|
}
|
|
static void
|
|
setup_windows (app_data *data,
|
|
GtkWidget *main,
|
|
GtkWidget *settings,
|
|
GtkWidget *emergency)
|
|
{
|
|
data->sync_win = main;
|
|
data->services_win = settings;
|
|
data->emergency_win = emergency;
|
|
gtk_window_set_transient_for (GTK_WINDOW (data->services_win),
|
|
GTK_WINDOW (data->sync_win));
|
|
gtk_window_set_transient_for (GTK_WINDOW (data->emergency_win),
|
|
GTK_WINDOW (data->sync_win));
|
|
g_signal_connect (data->services_win, "delete-event",
|
|
G_CALLBACK (gtk_widget_hide_on_delete), NULL);
|
|
g_signal_connect (data->emergency_win, "delete-event",
|
|
G_CALLBACK (gtk_widget_hide_on_delete), NULL);
|
|
}
|
|
|
|
static void
|
|
show_emergency_view (app_data *data)
|
|
{
|
|
update_emergency_view (data);
|
|
gtk_widget_hide (data->services_win);
|
|
gtk_window_present (GTK_WINDOW (data->emergency_win));
|
|
}
|
|
|
|
static void
|
|
show_services_list (app_data *data, const char *config_id_to_open)
|
|
{
|
|
g_free (data->config_id_to_open);
|
|
data->config_id_to_open = g_strdup (config_id_to_open);
|
|
|
|
gtk_widget_hide (data->emergency_win);
|
|
gtk_window_present (GTK_WINDOW (data->services_win));
|
|
update_services_list (data);
|
|
}
|
|
|
|
static void
|
|
show_main_view (app_data *data)
|
|
{
|
|
gtk_widget_hide (data->services_win);
|
|
gtk_widget_hide (data->emergency_win);
|
|
gtk_window_present (GTK_WINDOW (data->sync_win));
|
|
}
|
|
|
|
#endif
|
|
|
|
/* This is a hacky way to achieve autoscrolling when the expanders open/close */
|
|
static void
|
|
services_box_allocate_cb (GtkWidget *widget,
|
|
GtkAllocation *allocation,
|
|
app_data *data)
|
|
{
|
|
if (GTK_IS_WIDGET (data->expanded_config)) {
|
|
int y;
|
|
GtkAdjustment *adj;
|
|
GtkAllocation alloc;
|
|
|
|
gtk_widget_translate_coordinates (data->expanded_config,
|
|
data->services_box,
|
|
0, 0, NULL, &y);
|
|
gtk_widget_get_allocation (data->expanded_config, &alloc);
|
|
|
|
adj = gtk_scrolled_window_get_vadjustment
|
|
(GTK_SCROLLED_WINDOW (data->scrolled_window));
|
|
gtk_adjustment_clamp_page (adj, y, y + alloc.height);
|
|
|
|
data->expanded_config = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
info_bar_response_cb (GtkInfoBar *info_bar,
|
|
SyncErrorResponse response_id,
|
|
app_data *data)
|
|
{
|
|
switch (response_id) {
|
|
case SYNC_ERROR_RESPONSE_SYNC:
|
|
start_sync (data);
|
|
break;
|
|
case SYNC_ERROR_RESPONSE_EMERGENCY_SLOW_SYNC:
|
|
slow_sync (data);
|
|
break;
|
|
case SYNC_ERROR_RESPONSE_EMERGENCY:
|
|
show_emergency_view (data);
|
|
break;
|
|
case SYNC_ERROR_RESPONSE_SETTINGS_OPEN:
|
|
data->open_current = TRUE;
|
|
show_services_list (data, NULL);
|
|
break;
|
|
case SYNC_ERROR_RESPONSE_SETTINGS_SELECT:
|
|
show_services_list (data, NULL);
|
|
break;
|
|
default:
|
|
g_warn_if_reached ();
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
new_device_clicked_cb (GtkButton *btn, app_data *data)
|
|
{
|
|
DBusGProxy *proxy;
|
|
DBusGConnection *bus;
|
|
GError *error = NULL;
|
|
GAppInfo *info;
|
|
GAppLaunchContext *context;
|
|
|
|
switch (data->bluetooth_wizard) {
|
|
case SYNC_BLUETOOTH_MOBLIN:
|
|
|
|
bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
|
|
if (bus) {
|
|
proxy = dbus_g_proxy_new_for_name (bus,
|
|
"org.moblin.UX.Shell.Toolbar",
|
|
"/org/moblin/UX/Shell/Toolbar",
|
|
"org.moblin.UX.Shell.Toolbar");
|
|
dbus_g_proxy_call_no_reply (proxy, "ShowPanel",
|
|
G_TYPE_STRING, "bluetooth-panel",
|
|
G_TYPE_INVALID,
|
|
G_TYPE_INVALID);
|
|
g_object_unref (proxy);
|
|
}
|
|
break;
|
|
|
|
case SYNC_BLUETOOTH_GNOME:
|
|
info = (GAppInfo*) g_desktop_app_info_new ("bluetooth-wizard.desktop");
|
|
context = (GAppLaunchContext*) gdk_display_get_app_launch_context (
|
|
gtk_widget_get_display (data->sync_win));
|
|
g_app_info_launch (info, NULL, context, &error);
|
|
if (error) {
|
|
g_warning ("Failed to spawn bluetooth-wizard: %s", error->message);
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
g_object_unref (info);
|
|
g_object_unref (context);
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
static void
|
|
name_has_owner_cb (DBusGProxy *proxy, gboolean has_owner,
|
|
GError *error, app_data *data)
|
|
{
|
|
if (has_owner) {
|
|
gtk_widget_show (data->new_device_btn);
|
|
data->bluetooth_wizard = SYNC_BLUETOOTH_MOBLIN;
|
|
}
|
|
g_object_unref (proxy);
|
|
}
|
|
|
|
static void
|
|
init_bluetooth_ui (app_data *data)
|
|
{
|
|
char *bt_wizard;
|
|
DBusGConnection *bus;
|
|
DBusGProxy *proxy;
|
|
|
|
data->bluetooth_wizard = SYNC_BLUETOOTH_NONE;
|
|
|
|
/* look for gnome bluetooth wizard first */
|
|
bt_wizard = g_find_program_in_path ("bluetooth-wizard");
|
|
if (bt_wizard) {
|
|
gtk_widget_show (data->new_device_btn);
|
|
data->bluetooth_wizard = SYNC_BLUETOOTH_GNOME;
|
|
g_free (bt_wizard);
|
|
} else {
|
|
/* try Moblin shell next (bluetooth panel integrates bt wizard) */
|
|
bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
|
|
proxy = dbus_g_proxy_new_for_name (bus,
|
|
DBUS_SERVICE_DBUS,
|
|
DBUS_PATH_DBUS,
|
|
DBUS_INTERFACE_DBUS);
|
|
if (proxy) {
|
|
org_freedesktop_DBus_name_has_owner_async (proxy,
|
|
"org.moblin.UX.Shell.Toolbar",
|
|
(org_freedesktop_DBus_name_has_owner_reply)name_has_owner_cb,
|
|
data);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
autosync_toggle_cb (GtkWidget *widget, gpointer x, app_data *data)
|
|
{
|
|
if (data->current_service && data->current_service->config) {
|
|
gboolean new_active, old_active;
|
|
char *autosync = NULL;
|
|
|
|
new_active = gtk_switch_get_active (GTK_SWITCH (widget));
|
|
syncevo_config_get_value (data->current_service->config, NULL,
|
|
"autoSync", &autosync);
|
|
old_active = (g_strcmp0 (autosync, "1") == 0);
|
|
|
|
if (old_active != new_active) {
|
|
char *new_val;
|
|
operation_data *op_data;
|
|
|
|
new_val = new_active ? "1": "0";
|
|
syncevo_config_set_value (data->current_service->config, NULL,
|
|
"autoSync", new_val);
|
|
|
|
op_data = g_slice_new (operation_data);
|
|
op_data->data = data;
|
|
op_data->operation = OP_SAVE;
|
|
op_data->started = FALSE;
|
|
syncevo_server_start_no_sync_session (data->server,
|
|
data->current_service->name,
|
|
(SyncevoServerStartSessionCb)start_session_cb,
|
|
op_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
build_autosync_ui (app_data *data)
|
|
{
|
|
char *txt;
|
|
GtkWidget *lbl;
|
|
|
|
/* TRANSLATORS: label for checkbutton/toggle in main view.
|
|
* Please stick to similar length strings or break the line with
|
|
* "\n" if absolutely needed */
|
|
txt = _("Automatic sync");
|
|
|
|
|
|
lbl = gtk_label_new (txt);
|
|
gtk_widget_show (lbl);
|
|
gtk_box_pack_end (GTK_BOX (data->autosync_box), lbl, FALSE, FALSE, 0);
|
|
|
|
data->autosync_toggle = gtk_switch_new ();
|
|
gtk_widget_show (data->autosync_toggle);
|
|
gtk_box_pack_end (GTK_BOX (data->autosync_box), data->autosync_toggle,
|
|
FALSE, FALSE, 0);
|
|
g_signal_connect (data->autosync_toggle, "notify::active",
|
|
G_CALLBACK (autosync_toggle_cb), data);
|
|
gtk_widget_show (data->autosync_toggle);
|
|
}
|
|
|
|
static void
|
|
glade_name_workaround (GtkBuilder *builder, const char *name)
|
|
{
|
|
GtkWidget *w;
|
|
|
|
w = GTK_WIDGET (gtk_builder_get_object (builder, name));
|
|
if (w) {
|
|
gtk_widget_set_name (w, name);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
init_ui (app_data *data)
|
|
{
|
|
GtkBuilder *builder;
|
|
GError *error = NULL;
|
|
GtkWidget /* *frame, */ * service_error_box, *btn;
|
|
GtkAdjustment *adj;
|
|
GtkCssProvider *provider;
|
|
|
|
builder = gtk_builder_new ();
|
|
gtk_builder_add_from_file (builder, GLADEDIR "ui.xml", &error);
|
|
if (error) {
|
|
g_printerr ("Failed to load user interface from %s: %s\n",
|
|
GLADEDIR "ui.xml",
|
|
error->message);
|
|
g_error_free (error);
|
|
g_object_unref (builder);
|
|
return FALSE;
|
|
}
|
|
|
|
data->service_box = GTK_WIDGET (gtk_builder_get_object (builder, "service_box"));
|
|
service_error_box = GTK_WIDGET (gtk_builder_get_object (builder, "service_error_box"));
|
|
data->info_bar = gtk_info_bar_new ();
|
|
gtk_widget_set_no_show_all (data->info_bar, TRUE);
|
|
g_signal_connect (data->info_bar, "response",
|
|
G_CALLBACK (info_bar_response_cb), data);
|
|
gtk_box_pack_start (GTK_BOX (service_error_box), data->info_bar,
|
|
TRUE, TRUE, 16);
|
|
|
|
data->no_connection_box = GTK_WIDGET (gtk_builder_get_object (builder, "no_connection_box"));
|
|
data->server_icon_box = GTK_WIDGET (gtk_builder_get_object (builder, "server_icon_box"));
|
|
|
|
data->offline_label = GTK_WIDGET (gtk_builder_get_object (builder, "offline_label"));
|
|
data->progress = GTK_WIDGET (gtk_builder_get_object (builder, "progressbar"));
|
|
data->change_service_btn = GTK_WIDGET (gtk_builder_get_object (builder, "change_service_btn"));
|
|
data->emergency_btn = GTK_WIDGET (gtk_builder_get_object (builder, "emergency_btn"));
|
|
data->sync_btn = GTK_WIDGET (gtk_builder_get_object (builder, "sync_btn"));
|
|
data->sync_status_label = GTK_WIDGET (gtk_builder_get_object (builder, "sync_status_label"));
|
|
data->spinner_image = GTK_WIDGET (gtk_builder_get_object (builder, "spinner_image"));
|
|
gtk_image_set_from_file (GTK_IMAGE (data->spinner_image), THEMEDIR "sync-spinner.gif");
|
|
gtk_widget_set_no_show_all (data->spinner_image, TRUE);
|
|
gtk_widget_hide (data->spinner_image);
|
|
|
|
data->autosync_box = GTK_WIDGET (gtk_builder_get_object (builder, "autosync_box"));
|
|
build_autosync_ui (data);
|
|
|
|
data->server_label = GTK_WIDGET (gtk_builder_get_object (builder, "sync_service_label"));
|
|
data->last_synced_label = GTK_WIDGET (gtk_builder_get_object (builder, "last_synced_label"));
|
|
data->sources_box = GTK_WIDGET (gtk_builder_get_object (builder, "sources_box"));
|
|
|
|
data->new_service_btn = GTK_WIDGET (gtk_builder_get_object (builder, "new_service_btn"));
|
|
gtk_widget_set_size_request (data->new_service_btn,
|
|
SYNC_UI_LIST_BTN_WIDTH, SYNC_UI_LIST_ICON_SIZE);
|
|
g_signal_connect (data->new_service_btn, "clicked",
|
|
G_CALLBACK (setup_new_service_clicked), data);
|
|
|
|
/* service list view */
|
|
data->scrolled_window = GTK_WIDGET (gtk_builder_get_object (builder, "scrolledwindow"));
|
|
adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (data->scrolled_window));
|
|
data->services_box = GTK_WIDGET (gtk_builder_get_object (builder, "services_box"));
|
|
gtk_container_set_focus_vadjustment (GTK_CONTAINER (data->services_box), adj);
|
|
g_signal_connect(data->services_box, "size-allocate",
|
|
G_CALLBACK (services_box_allocate_cb), data);
|
|
|
|
data->devices_box = GTK_WIDGET (gtk_builder_get_object (builder, "devices_box"));
|
|
data->settings_close_btn = GTK_WIDGET (gtk_builder_get_object (builder, "settings_close_btn"));
|
|
|
|
/* emergency view */
|
|
btn = GTK_WIDGET (gtk_builder_get_object (builder, "slow_sync_btn"));
|
|
g_signal_connect (btn, "clicked",
|
|
G_CALLBACK (slow_sync_clicked_cb), data);
|
|
data->refresh_from_server_btn_label = GTK_WIDGET (gtk_builder_get_object (builder, "refresh_from_server_btn_label"));
|
|
g_signal_connect (GTK_WIDGET (gtk_builder_get_object (builder, "refresh_from_server_btn")), "clicked",
|
|
G_CALLBACK (refresh_from_server_clicked_cb), data);
|
|
data->refresh_from_client_btn_label = GTK_WIDGET (gtk_builder_get_object (builder, "refresh_from_client_btn_label"));
|
|
g_signal_connect (GTK_WIDGET (gtk_builder_get_object (builder, "refresh_from_client_btn")), "clicked",
|
|
G_CALLBACK (refresh_from_client_clicked_cb), data);
|
|
|
|
data->emergency_label = GTK_WIDGET (gtk_builder_get_object (builder, "emergency_label"));
|
|
data->emergency_expander = GTK_WIDGET (gtk_builder_get_object (builder, "emergency_expander"));
|
|
data->emergency_source_table = GTK_WIDGET (gtk_builder_get_object (builder, "emergency_source_table"));
|
|
data->emergency_backup_table = GTK_WIDGET (gtk_builder_get_object (builder, "emergency_backup_table"));
|
|
data->emergency_close_btn = GTK_WIDGET (gtk_builder_get_object (builder, "emergency_close_btn"));
|
|
|
|
/* No (documented) way to add own widgets to gtkbuilder it seems...
|
|
swap the all dummy widgets with Muxwidgets */
|
|
setup_windows (data,
|
|
GTK_WIDGET (gtk_builder_get_object (builder, "sync_win")),
|
|
GTK_WIDGET (gtk_builder_get_object (builder, "services_win")),
|
|
GTK_WIDGET (gtk_builder_get_object (builder, "emergency_win")));
|
|
|
|
data->main_frame = switch_dummy_to_mux_frame (GTK_WIDGET (gtk_builder_get_object (builder, "main_frame")));
|
|
data->log_frame = switch_dummy_to_mux_frame (GTK_WIDGET (gtk_builder_get_object (builder, "log_frame")));
|
|
/* frame = */ switch_dummy_to_mux_frame (GTK_WIDGET (gtk_builder_get_object (builder, "services_list_frame")));
|
|
/* frame = */ switch_dummy_to_mux_frame (GTK_WIDGET (gtk_builder_get_object (builder, "emergency_frame")));
|
|
|
|
g_signal_connect (data->sync_win, "destroy",
|
|
G_CALLBACK (gtk_main_quit), NULL);
|
|
g_signal_connect (data->change_service_btn, "clicked",
|
|
G_CALLBACK (change_service_clicked_cb), data);
|
|
g_signal_connect (data->emergency_btn, "clicked",
|
|
G_CALLBACK (emergency_clicked_cb), data);
|
|
g_signal_connect (data->sync_btn, "clicked",
|
|
G_CALLBACK (sync_clicked_cb), data);
|
|
g_signal_connect_swapped (data->emergency_close_btn, "clicked",
|
|
G_CALLBACK (show_main_view), data);
|
|
g_signal_connect_swapped (data->settings_close_btn, "clicked",
|
|
G_CALLBACK (show_main_view), data);
|
|
g_signal_connect (data->emergency_btn, "clicked",
|
|
G_CALLBACK (emergency_clicked_cb), data);
|
|
|
|
data->new_device_btn = GTK_WIDGET (gtk_builder_get_object (builder, "new_device_btn"));
|
|
g_signal_connect (data->new_device_btn, "clicked",
|
|
G_CALLBACK (new_device_clicked_cb), data);
|
|
|
|
/* workarounds for glade not working with gtkbuilder >= 2.20:
|
|
* widgets do not get names. */
|
|
glade_name_workaround (builder, "meego_win");
|
|
glade_name_workaround (builder, "sync_data_and_type_box");
|
|
glade_name_workaround (builder, "log_frame");
|
|
glade_name_workaround (builder, "backup_frame");
|
|
glade_name_workaround (builder, "services_frame");
|
|
glade_name_workaround (builder, "sync_service_label");
|
|
glade_name_workaround (builder, "sync_status_label");
|
|
glade_name_workaround (builder, "no_server_label");
|
|
glade_name_workaround (builder, "sync_failure_label");
|
|
glade_name_workaround (builder, "sync_btn");
|
|
|
|
init_bluetooth_ui (data);
|
|
|
|
/* custom styling */
|
|
provider = gtk_css_provider_new ();
|
|
if (gtk_css_provider_load_from_path (provider, THEMEDIR "sync-ui.css", &error)) {
|
|
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
|
|
GTK_STYLE_PROVIDER (provider),
|
|
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
|
g_object_unref (provider);
|
|
} else {
|
|
g_printerr ("Failed to load styles from %s: %s\n",
|
|
THEMEDIR "sync-ui.css",
|
|
error->message);
|
|
g_error_free (error);
|
|
error = NULL;
|
|
}
|
|
|
|
g_object_unref (builder);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
load_icon (const char *uri, GtkBox *icon_box, guint icon_size)
|
|
{
|
|
GError *error = NULL;
|
|
GdkPixbuf *pixbuf;
|
|
GtkWidget *image;
|
|
const char *filename;
|
|
|
|
if (uri && strlen (uri) > 0) {
|
|
if (g_str_has_prefix (uri, "file://")) {
|
|
filename = uri+7;
|
|
} else {
|
|
g_warning ("only file:// icon uri is supported: %s", uri);
|
|
filename = THEMEDIR "sync-generic.png";
|
|
}
|
|
} else {
|
|
filename = THEMEDIR "sync-generic.png";
|
|
}
|
|
pixbuf = gdk_pixbuf_new_from_file_at_scale (filename,
|
|
icon_size, icon_size,
|
|
TRUE, &error);
|
|
|
|
if (!pixbuf) {
|
|
g_warning ("Failed to load service icon: %s", error->message);
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
|
|
image = gtk_image_new_from_pixbuf (pixbuf);
|
|
gtk_widget_set_size_request (image, icon_size, icon_size);
|
|
g_object_unref (pixbuf);
|
|
gtk_container_foreach (GTK_CONTAINER(icon_box),
|
|
(GtkCallback)remove_child,
|
|
icon_box);
|
|
gtk_box_pack_start (icon_box, image, FALSE, FALSE, 0);
|
|
gtk_widget_show (image);
|
|
}
|
|
|
|
static void
|
|
emergency_toggle_notify_active_cb (GtkWidget *widget,
|
|
gpointer p,
|
|
app_data *data)
|
|
{
|
|
char *source;
|
|
|
|
source = g_object_get_data (G_OBJECT (widget), "source");
|
|
|
|
g_return_if_fail (source);
|
|
|
|
if (gtk_switch_get_active (GTK_SWITCH (widget))) {
|
|
g_hash_table_insert (data->emergency_sources, g_strdup (source), "");
|
|
} else {
|
|
g_hash_table_remove (data->emergency_sources, source);
|
|
}
|
|
update_emergency_expander (data);
|
|
}
|
|
|
|
static GtkWidget*
|
|
add_emergency_toggle_widget (app_data *data,
|
|
const char *title,
|
|
gboolean active,
|
|
guint row, guint col)
|
|
{
|
|
GtkWidget *toggle;
|
|
|
|
GtkWidget *label;
|
|
col = col * 2;
|
|
label = gtk_label_new (title);
|
|
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
|
|
gtk_widget_show (label);
|
|
gtk_table_attach (GTK_TABLE (data->emergency_source_table), label,
|
|
col, col + 1, row, row + 1,
|
|
GTK_FILL, GTK_FILL, 16, 0);
|
|
toggle = gtk_switch_new ();
|
|
gtk_switch_set_active (GTK_SWITCH (toggle), active);
|
|
g_signal_connect (toggle, "notify::active",
|
|
G_CALLBACK (emergency_toggle_notify_active_cb), data);
|
|
gtk_widget_show (toggle);
|
|
gtk_table_attach (GTK_TABLE (data->emergency_source_table), toggle,
|
|
col + 1, col + 2, row, row + 1,
|
|
GTK_FILL, GTK_FILL, 0, 0);
|
|
return toggle;
|
|
}
|
|
|
|
static void
|
|
update_emergency_expander (app_data *data)
|
|
{
|
|
char *text, *sources = NULL;
|
|
GHashTableIter iter;
|
|
char *name;
|
|
|
|
g_hash_table_iter_init (&iter, data->emergency_sources);
|
|
while (g_hash_table_iter_next (&iter, (gpointer)&name, NULL)) {
|
|
char *pretty, *tmp;
|
|
pretty = get_pretty_source_name (name);
|
|
if (sources) {
|
|
tmp = g_strdup_printf ("%s, %s", sources, pretty);
|
|
g_free (sources);
|
|
g_free (pretty);
|
|
sources = tmp;
|
|
} else {
|
|
sources = pretty;
|
|
}
|
|
}
|
|
if (sources) {
|
|
/* This is the expander label in emergency view. It summarizes the
|
|
* currently selected data sources. First placeholder is service/device
|
|
* name, second a comma separeted list of sources.
|
|
* E.g. "Affected data: Google Contacts, Appointments" */
|
|
text = g_strdup_printf (_("Affected data: %s %s"),
|
|
data->current_service->pretty_name,
|
|
sources);
|
|
g_free (sources);
|
|
} else {
|
|
text = g_strdup_printf (_("Affected data: none"));
|
|
}
|
|
|
|
gtk_expander_set_label (GTK_EXPANDER (data->emergency_expander), text);
|
|
g_free (text);
|
|
}
|
|
|
|
static void
|
|
add_emergency_source (const char *name, GHashTable *source, app_data *data)
|
|
{
|
|
source_config *conf;
|
|
GtkWidget *toggle;
|
|
guint rows, cols;
|
|
guint row;
|
|
static guint col;
|
|
gboolean active = TRUE;
|
|
char *pretty_name;
|
|
|
|
conf = g_hash_table_lookup (data->current_service->source_configs,
|
|
name);
|
|
g_object_get (data->emergency_source_table,
|
|
"n-rows", &rows,
|
|
"n-columns", &cols,
|
|
NULL);
|
|
if (cols != 1 && col == 0){
|
|
col = 1;
|
|
row = rows - 1;
|
|
} else {
|
|
col = 0;
|
|
row = rows;
|
|
}
|
|
|
|
active = (g_hash_table_lookup (data->emergency_sources, name) != NULL);
|
|
|
|
pretty_name = get_pretty_source_name (name);
|
|
toggle = add_emergency_toggle_widget (data, pretty_name, active, row, col);
|
|
gtk_widget_set_sensitive (toggle, source_config_is_usable (conf));
|
|
g_object_set_data_full (G_OBJECT (toggle), "source", g_strdup (name), g_free);
|
|
g_free (pretty_name);
|
|
}
|
|
|
|
static void
|
|
update_backup_visibilities (app_data *data)
|
|
{
|
|
char *key;
|
|
GHashTableIter iter;
|
|
GList *l, *widgets;
|
|
|
|
widgets = gtk_container_get_children (
|
|
GTK_CONTAINER (data->emergency_backup_table));
|
|
gtk_widget_show_all (data->emergency_backup_table);
|
|
|
|
/* hide backup widgets that do not contain selected sources */
|
|
g_hash_table_iter_init (&iter, data->emergency_sources);
|
|
while (g_hash_table_iter_next (&iter, (gpointer)&key, NULL)) {
|
|
for (l = widgets; l; l = l->next) {
|
|
if (!g_object_get_data (G_OBJECT (l->data), key)) {
|
|
gtk_widget_hide (GTK_WIDGET (l->data));
|
|
}
|
|
}
|
|
}
|
|
|
|
g_list_free (widgets);
|
|
}
|
|
|
|
static void
|
|
restore_clicked_cb (GtkButton *btn, app_data *data)
|
|
{
|
|
const char *dir, *time_str;
|
|
operation_data *op_data;
|
|
char *message;
|
|
|
|
dir = g_object_get_data (G_OBJECT (btn), "dir");
|
|
time_str = g_object_get_data (G_OBJECT (btn), "time");
|
|
g_return_if_fail (dir && time_str);
|
|
|
|
/* TRANSLATORS: confirmation for restoring a backup. placeholder is the
|
|
* backup time string defined below */
|
|
message = g_strdup_printf (_("Do you want to restore the backup from %s? "
|
|
"All changes you have made since then will be lost."),
|
|
time_str);
|
|
if (!show_confirmation (data->sync_win, message, _("Yes, restore"), _("No"))) {
|
|
g_free (message);
|
|
return;
|
|
}
|
|
g_free (message);
|
|
|
|
op_data = g_slice_new (operation_data);
|
|
op_data->data = data;
|
|
op_data->operation = OP_RESTORE;
|
|
op_data->dir = dir;
|
|
op_data->started = FALSE;
|
|
syncevo_server_start_session (data->server,
|
|
data->current_service->name,
|
|
(SyncevoServerStartSessionCb)start_session_cb,
|
|
op_data);
|
|
|
|
show_main_view (data);
|
|
}
|
|
|
|
static void
|
|
add_backup (app_data *data, const char *peername, const char *dir,
|
|
long endtime, GList *sources)
|
|
{
|
|
GtkWidget *timelabel, *label, *blabel, *button, *box;;
|
|
guint rows;
|
|
char *text;
|
|
char time_str[60];
|
|
struct tm *tim;
|
|
|
|
tim = localtime (&endtime);
|
|
/* TRANSLATORS: date/time for strftime(), used in emergency view backup
|
|
* label. Any time format that shows date and time is good. */
|
|
strftime (time_str, sizeof (time_str), _("%x %X"), tim);
|
|
|
|
g_object_get (data->emergency_backup_table,
|
|
"n-rows", &rows,
|
|
NULL);
|
|
|
|
box = gtk_vbox_new (TRUE, 6);
|
|
gtk_table_attach (GTK_TABLE (data->emergency_backup_table), box,
|
|
0, 1, rows, rows + 1,
|
|
GTK_EXPAND|GTK_FILL, GTK_FILL, 16, 0);
|
|
|
|
timelabel = gtk_label_new (time_str);
|
|
gtk_misc_set_alignment (GTK_MISC (timelabel), 0.0, 0.5);
|
|
gtk_label_set_line_wrap (GTK_LABEL (timelabel), TRUE);
|
|
gtk_widget_set_size_request (timelabel, 600, -1);
|
|
gtk_box_pack_start (GTK_BOX (box), timelabel, TRUE, TRUE, 0);
|
|
|
|
/* TRANSLATORS: label for a backup in emergency view. Placeholder is
|
|
* service or device name */
|
|
text = g_strdup_printf (_("Backed up before syncing with %s"), peername);
|
|
label = gtk_label_new (text);
|
|
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
|
|
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
|
|
gtk_widget_set_size_request (label, 600, -1);
|
|
gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
|
|
g_free (text);
|
|
|
|
button = gtk_button_new ();
|
|
gtk_table_attach (GTK_TABLE (data->emergency_backup_table), button,
|
|
1, 2, rows, rows + 1,
|
|
GTK_FILL, GTK_SHRINK, 32, 0);
|
|
g_object_set_data_full (G_OBJECT (button), "dir", g_strdup(dir), g_free);
|
|
g_object_set_data_full (G_OBJECT (button), "time", g_strdup(time_str), g_free);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (restore_clicked_cb), data);
|
|
|
|
blabel = gtk_label_new (_("Restore"));
|
|
gtk_misc_set_padding (GTK_MISC (blabel), 32, 0);
|
|
gtk_container_add (GTK_CONTAINER (button), blabel);
|
|
|
|
for (; sources; sources = sources->next) {
|
|
g_object_set_data (G_OBJECT (box), (char *)sources->data, "");
|
|
g_object_set_data (G_OBJECT (button), (char *)sources->data, "");
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_reports_for_backups_cb (SyncevoServer *server,
|
|
SyncevoReports *reports,
|
|
GError *error,
|
|
app_data *data)
|
|
{
|
|
guint len, i;
|
|
|
|
if (error) {
|
|
g_warning ("Error in Session.GetReports: %s", error->message);
|
|
g_error_free (error);
|
|
/* non-fatal, unknown error */
|
|
return;
|
|
}
|
|
|
|
len = syncevo_reports_get_length (reports);
|
|
for (i = 0; i < len; i++) {
|
|
GHashTable *report = syncevo_reports_index (reports, i);
|
|
GHashTableIter iter;
|
|
char *key, *val;
|
|
/* long status = -1; */
|
|
long endtime = -1;
|
|
char *peername = NULL;
|
|
char *dir = NULL;
|
|
GList *backup_sources = NULL;
|
|
|
|
g_hash_table_iter_init (&iter, report);
|
|
while (g_hash_table_iter_next (&iter, (gpointer)&key, (gpointer)&val)) {
|
|
char **strs;
|
|
|
|
strs = g_strsplit (key, "-", 6);
|
|
if (!strs) {
|
|
continue;
|
|
}
|
|
|
|
if (g_strcmp0 (strs[0], "source") == 0 &&
|
|
g_strcmp0 (strs[2], "backup") == 0 &&
|
|
g_strcmp0 (strs[3], "before") == 0) {
|
|
backup_sources = g_list_prepend (backup_sources,
|
|
g_strdup (strs[1]));
|
|
} else if (g_strcmp0 (strs[0], "end") == 0) {
|
|
endtime = strtol (val, NULL, 10);
|
|
} else if (g_strcmp0 (strs[0], "status") == 0) {
|
|
/* status = strtol (val, NULL, 10); */
|
|
} else if (g_strcmp0 (strs[0], "peer") == 0) {
|
|
peername = val;
|
|
} else if (g_strcmp0 (strs[0], "dir") == 0) {
|
|
dir = val;
|
|
}
|
|
g_strfreev (strs);
|
|
}
|
|
|
|
if (peername && dir && endtime > 0) {
|
|
add_backup (data, peername, dir, endtime, backup_sources);
|
|
}
|
|
g_list_foreach (backup_sources, (GFunc)g_free, NULL);
|
|
g_list_free (backup_sources);
|
|
}
|
|
|
|
data->backup_count += len;
|
|
if (len == REPORTS_PER_CALL) {
|
|
syncevo_server_get_reports (data->server,
|
|
"",
|
|
data->backup_count, REPORTS_PER_CALL,
|
|
(SyncevoServerGetReportsCb)get_reports_for_backups_cb,
|
|
data);
|
|
}
|
|
|
|
update_backup_visibilities (data);
|
|
}
|
|
|
|
static const char*
|
|
get_syncevo_context (const char *config_name)
|
|
{
|
|
char *context;
|
|
|
|
context = g_strrstr (config_name, "@");
|
|
if (!context) {
|
|
context = "";
|
|
}
|
|
return context;
|
|
}
|
|
|
|
static void
|
|
update_emergency_view (app_data *data)
|
|
{
|
|
char *text;
|
|
|
|
if (!data->current_service) {
|
|
g_warning ("no service defined in Emergency view");
|
|
return;
|
|
}
|
|
|
|
if (data->forced_emergency) {
|
|
text = g_strdup_printf (
|
|
/* TRANSLATORS: this is an explanation in Emergency view.
|
|
* Placeholder is a service/device name */
|
|
_("A normal sync with %s is not possible at this time. "
|
|
"You can do a slow two-way sync or start from scratch. You "
|
|
"can also restore a backup, but a slow sync or starting from "
|
|
"scratch will still be required before normal sync is "
|
|
"possible."),
|
|
data->current_service->pretty_name);
|
|
} else {
|
|
/* TRANSLATORS: this is an explanation in Emergency view.
|
|
* Placeholder is a service/device name */
|
|
text = g_strdup_printf (
|
|
_("If something has gone horribly wrong, you can try a "
|
|
"slow sync, start from scratch or restore from backup."));
|
|
}
|
|
gtk_label_set_text (GTK_LABEL (data->emergency_label), text);
|
|
g_free (text);
|
|
|
|
/* TRANSLATORS: These are a buttons in Emergency view. Placeholder is a
|
|
* service/device name. Please don't use too long lines, but feel free to
|
|
* use several lines. */
|
|
text = g_strdup_printf (_("Delete all your local\n"
|
|
"data and replace with\n"
|
|
"data from %s"),
|
|
data->current_service->pretty_name);
|
|
gtk_label_set_text (GTK_LABEL (data->refresh_from_server_btn_label), text);
|
|
g_free (text);
|
|
text = g_strdup_printf (_("Delete all data on\n"
|
|
"%s and replace\n"
|
|
"with your local data"),
|
|
data->current_service->pretty_name);
|
|
gtk_label_set_text (GTK_LABEL (data->refresh_from_client_btn_label), text);
|
|
g_free (text);
|
|
|
|
gtk_container_foreach (GTK_CONTAINER (data->emergency_source_table),
|
|
(GtkCallback)remove_child,
|
|
data->emergency_source_table);
|
|
gtk_table_resize (GTK_TABLE (data->emergency_source_table), 1, 1);
|
|
|
|
/* using this instead of current_service->source_configs
|
|
* to get the same order as the configuration has... */
|
|
syncevo_config_foreach_source (data->current_service->config,
|
|
(ConfigFunc)add_emergency_source,
|
|
data);
|
|
update_emergency_expander (data);
|
|
|
|
data->backup_count = 0;
|
|
gtk_container_foreach (GTK_CONTAINER (data->emergency_backup_table),
|
|
(GtkCallback)remove_child,
|
|
data->emergency_backup_table);
|
|
gtk_table_resize (GTK_TABLE (data->emergency_backup_table), 1, 1);
|
|
syncevo_server_get_reports (data->server,
|
|
get_syncevo_context (data->current_service->name),
|
|
0, REPORTS_PER_CALL,
|
|
(SyncevoServerGetReportsCb)get_reports_for_backups_cb,
|
|
data);
|
|
|
|
}
|
|
|
|
static void
|
|
update_service_source_ui (const char *name, source_config *conf, app_data *data)
|
|
{
|
|
GtkWidget *lbl, *box;
|
|
char *pretty_name, *title;
|
|
|
|
if (!source_config_is_usable (conf)) {
|
|
return;
|
|
}
|
|
|
|
conf->box = gtk_vbox_new (FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (data->sources_box), conf->box,
|
|
FALSE, FALSE, 8);
|
|
|
|
pretty_name = get_pretty_source_name_markup (name);
|
|
title = g_strdup_printf ("<b>%s</b>", pretty_name);
|
|
lbl = gtk_label_new (NULL);
|
|
gtk_label_set_markup (GTK_LABEL (lbl), title);
|
|
g_free (pretty_name);
|
|
g_free (title);
|
|
gtk_misc_set_alignment (GTK_MISC (lbl), 0.0, 0.5);
|
|
gtk_box_pack_start (GTK_BOX (conf->box), lbl, TRUE, TRUE, 0);
|
|
|
|
box = gtk_hbox_new (FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (conf->box), box, FALSE, FALSE, 0);
|
|
|
|
conf->info_bar = gtk_info_bar_new ();
|
|
gtk_box_pack_start (GTK_BOX (box), conf->info_bar, TRUE, TRUE, 16);
|
|
gtk_widget_set_no_show_all (conf->info_bar, TRUE);
|
|
g_signal_connect (conf->info_bar, "response",
|
|
G_CALLBACK (info_bar_response_cb), data);
|
|
|
|
conf->label = gtk_label_new (NULL);
|
|
gtk_misc_set_alignment (GTK_MISC (conf->label), 0.0, 0.5);
|
|
gtk_box_pack_start (GTK_BOX (conf->box), conf->label, TRUE, TRUE, 0);
|
|
|
|
source_config_update_widget (conf);
|
|
|
|
gtk_widget_show_all (conf->box);
|
|
}
|
|
|
|
static void
|
|
check_source_cb (SyncevoSession *session,
|
|
GError *error,
|
|
source_config *source)
|
|
{
|
|
if (error) {
|
|
if(error->code == DBUS_GERROR_REMOTE_EXCEPTION &&
|
|
dbus_g_error_has_name (error, SYNCEVO_DBUS_ERROR_SOURCE_UNUSABLE)) {
|
|
/* source is not supported locally */
|
|
if (source) {
|
|
source->supported_locally = FALSE;
|
|
if (source->box) {
|
|
/* widget is already on screen, hide it */
|
|
gtk_widget_hide (source->box);
|
|
}
|
|
}
|
|
} else {
|
|
g_warning ("CheckSource failed: %s", error->message);
|
|
/* non-fatal, unknown error */
|
|
}
|
|
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
update_service_ui (app_data *data)
|
|
{
|
|
char *icon_uri = NULL;
|
|
char *autosync = NULL;
|
|
|
|
gtk_container_foreach (GTK_CONTAINER (data->sources_box),
|
|
(GtkCallback)remove_child,
|
|
data->sources_box);
|
|
|
|
if (data->current_service && data->current_service->config) {
|
|
syncevo_config_get_value (data->current_service->config,
|
|
NULL, "IconURI", &icon_uri);
|
|
syncevo_config_get_value (data->current_service->config,
|
|
NULL, "autoSync", &autosync);
|
|
|
|
g_hash_table_foreach (data->current_service->source_configs,
|
|
(GHFunc)update_service_source_ui,
|
|
data);
|
|
}
|
|
load_icon (icon_uri,
|
|
GTK_BOX (data->server_icon_box),
|
|
SYNC_UI_ICON_SIZE);
|
|
|
|
gtk_switch_set_active (GTK_SWITCH (data->autosync_toggle),
|
|
g_strcmp0 (autosync, "1") == 0);
|
|
|
|
refresh_last_synced_label (data);
|
|
|
|
gtk_widget_show_all (data->sources_box);
|
|
}
|
|
|
|
static void
|
|
unexpand_config_widget (GtkWidget *w, GtkWidget *exception)
|
|
{
|
|
if (SYNC_IS_CONFIG_WIDGET (w) && exception && exception != w) {
|
|
sync_config_widget_set_expanded (SYNC_CONFIG_WIDGET (w), FALSE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
config_widget_expanded_cb (GtkWidget *widget, GParamSpec *pspec, app_data *data)
|
|
{
|
|
if (sync_config_widget_get_expanded (SYNC_CONFIG_WIDGET (widget))) {
|
|
data->expanded_config = widget;
|
|
gtk_container_foreach (GTK_CONTAINER (data->services_box),
|
|
(GtkCallback)unexpand_config_widget,
|
|
widget);
|
|
}
|
|
}
|
|
|
|
static void
|
|
config_widget_changed_cb (GtkWidget *widget, app_data *data)
|
|
{
|
|
if (sync_config_widget_get_current (SYNC_CONFIG_WIDGET (widget))) {
|
|
const char *name = NULL;
|
|
name = sync_config_widget_get_name (SYNC_CONFIG_WIDGET (widget));
|
|
reload_config (data, name);
|
|
show_main_view (data);
|
|
} else {
|
|
reload_config (data, NULL);
|
|
update_services_list (data);
|
|
}
|
|
}
|
|
|
|
static SyncConfigWidget*
|
|
add_configuration_to_box (GtkBox *box,
|
|
SyncevoConfig *config,
|
|
const char *name,
|
|
gboolean has_template,
|
|
gboolean has_configuration,
|
|
app_data *data)
|
|
{
|
|
GtkWidget *item = NULL;
|
|
gboolean current = FALSE;
|
|
const char *current_name = NULL;
|
|
|
|
if (data->current_service) {
|
|
current_name = data->current_service->pretty_name;
|
|
if (data->current_service->name && name &&
|
|
g_ascii_strcasecmp (name, data->current_service->name) == 0) {
|
|
current = TRUE;
|
|
}
|
|
}
|
|
|
|
item = sync_config_widget_new (data->server, name,
|
|
config,
|
|
current, current_name,
|
|
has_configuration, has_template);
|
|
g_signal_connect (item, "changed",
|
|
G_CALLBACK (config_widget_changed_cb), data);
|
|
g_signal_connect (item, "notify::expanded",
|
|
G_CALLBACK (config_widget_expanded_cb), data);
|
|
gtk_widget_show (item);
|
|
gtk_box_pack_start (box, item, FALSE, FALSE, 0);
|
|
|
|
if (current) {
|
|
sync_config_widget_set_expanded (SYNC_CONFIG_WIDGET (item),
|
|
data->open_current);
|
|
}
|
|
if (g_strcmp0 (name, "default") == 0) {
|
|
sync_config_widget_set_expanded (SYNC_CONFIG_WIDGET (item),
|
|
TRUE);
|
|
}
|
|
|
|
if (data->config_id_to_open) {
|
|
sync_config_widget_expand_id (SYNC_CONFIG_WIDGET (item),
|
|
data->config_id_to_open);
|
|
}
|
|
|
|
return SYNC_CONFIG_WIDGET (item);
|
|
|
|
}
|
|
|
|
static void
|
|
find_new_service_config (SyncConfigWidget *w, GtkWidget **found)
|
|
{
|
|
if (SYNC_IS_CONFIG_WIDGET (w)) {
|
|
if (!sync_config_widget_get_configured (w) &&
|
|
!sync_config_widget_get_has_template (w)) {
|
|
*found = GTK_WIDGET (w);
|
|
}
|
|
}
|
|
}
|
|
|
|
typedef struct config_data {
|
|
app_data *data;
|
|
char *name;
|
|
gboolean has_configuration;
|
|
gboolean has_template;
|
|
GHashTable *device_templates;
|
|
|
|
} config_data;
|
|
|
|
#define LEGAL_CONFIG_NAME_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ1234567890-_"
|
|
|
|
static void
|
|
get_config_for_config_widget_cb (SyncevoServer *server,
|
|
SyncevoConfig *config,
|
|
GError *error,
|
|
config_data *c_data)
|
|
{
|
|
char *ready, *is_peer, *url, *type;
|
|
|
|
c_data->data->service_list_updates_left--;
|
|
|
|
if (error) {
|
|
/* show in UI? */
|
|
g_warning ("Server.GetConfig() failed: %s", error->message);
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
|
|
syncevo_config_get_value (config, NULL, "ConsumerReady", &ready);
|
|
syncevo_config_get_value (config, NULL, "PeerIsClient", &is_peer);
|
|
syncevo_config_get_value (config, NULL, "syncURL", &url);
|
|
syncevo_config_get_value (config, NULL, "peerType", &type);
|
|
|
|
|
|
if (g_strcmp0 ("1", ready) != 0 ||
|
|
(type && g_strcmp0 ("WebDAV", type) == 0) ||
|
|
(url && g_str_has_prefix (url, "local://@"))) {
|
|
|
|
/* Ignore existing configs and templates unless they are
|
|
explicitly marked as "ConsumerReady.
|
|
Also ignore webdav (and the local syncs used for webdav)
|
|
for now */
|
|
} else if (is_peer && g_strcmp0 ("1", is_peer) == 0) {
|
|
if (url) {
|
|
SyncConfigWidget *w;
|
|
char *fp, *tmp, *template_name, *device_name = NULL;
|
|
char **fpv = NULL;
|
|
|
|
syncevo_config_get_value (config, NULL, "deviceName", &tmp);
|
|
if (!tmp) {
|
|
device_name = g_strdup (c_data->name);
|
|
} else {
|
|
device_name = g_strcanon (g_strdup (tmp), LEGAL_CONFIG_NAME_CHARS, '-');
|
|
}
|
|
|
|
|
|
syncevo_config_get_value (config, NULL, "templateName", &template_name);
|
|
if (!template_name) {
|
|
syncevo_config_get_value (config, NULL, "fingerPrint", &fp);
|
|
if (fp) {
|
|
fpv = g_strsplit_set (fp, ",;", 2);
|
|
if (g_strv_length (fpv) > 0) {
|
|
template_name = fpv[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* keep a list of added devices */
|
|
w = g_hash_table_lookup (c_data->device_templates, url);
|
|
if (!w) {
|
|
w = add_configuration_to_box (GTK_BOX (c_data->data->devices_box),
|
|
config,
|
|
device_name,
|
|
c_data->has_template,
|
|
c_data->has_configuration,
|
|
c_data->data);
|
|
g_hash_table_insert (c_data->device_templates, url, w);
|
|
sync_config_widget_add_alternative_config (w, template_name, config,
|
|
c_data->has_configuration);
|
|
} else {
|
|
/* TODO: might want to add a new widget, if user has created more
|
|
* configs for same device: this really requires us to look at
|
|
* all configs / templates, then decide what to sho w*/
|
|
|
|
/* there is a widget for this device already, add this info there*/
|
|
sync_config_widget_add_alternative_config (w, template_name, config,
|
|
c_data->has_configuration);
|
|
}
|
|
g_free (device_name);
|
|
g_strfreev (fpv);
|
|
}
|
|
} else {
|
|
add_configuration_to_box (GTK_BOX (c_data->data->services_box),
|
|
config,
|
|
c_data->name,
|
|
c_data->has_template,
|
|
c_data->has_configuration,
|
|
c_data->data);
|
|
}
|
|
|
|
g_free (c_data->name);
|
|
g_hash_table_unref (c_data->device_templates);
|
|
g_slice_free (config_data, c_data);
|
|
}
|
|
|
|
static void
|
|
get_config_for_config_widget (app_data *data,
|
|
const char *config,
|
|
gboolean has_template,
|
|
gboolean has_configuration,
|
|
GHashTable *device_templates)
|
|
|
|
{
|
|
config_data *c_data;
|
|
|
|
data->service_list_updates_left++;
|
|
|
|
c_data = g_slice_new0 (config_data);
|
|
c_data->data = data;
|
|
c_data->name = g_strdup (config);
|
|
c_data->has_template = has_template;
|
|
c_data->has_configuration = has_configuration;
|
|
if (device_templates) {
|
|
c_data->device_templates = g_hash_table_ref (device_templates);
|
|
}
|
|
|
|
syncevo_server_get_config (data->server,
|
|
config,
|
|
!has_configuration,
|
|
(SyncevoServerGetConfigCb)get_config_for_config_widget_cb,
|
|
c_data);
|
|
}
|
|
|
|
static void
|
|
setup_new_service_clicked (GtkButton *btn, app_data *data)
|
|
{
|
|
GtkWidget *widget = NULL;
|
|
|
|
gtk_container_foreach (GTK_CONTAINER (data->services_box),
|
|
(GtkCallback)unexpand_config_widget,
|
|
NULL);
|
|
|
|
/* if a new service config has already been added, use that.
|
|
* Otherwise add one. */
|
|
gtk_container_foreach (GTK_CONTAINER (data->services_box),
|
|
(GtkCallback)find_new_service_config,
|
|
&widget);
|
|
if (!widget) {
|
|
get_config_for_config_widget (data, "default", TRUE, FALSE, NULL);
|
|
} else {
|
|
sync_config_widget_set_expanded (SYNC_CONFIG_WIDGET (widget), TRUE);
|
|
}
|
|
}
|
|
|
|
typedef struct templates_data {
|
|
app_data *data;
|
|
char **templates;
|
|
} templates_data;
|
|
|
|
static void
|
|
get_configs_cb (SyncevoServer *server,
|
|
char **configs,
|
|
GError *error,
|
|
templates_data *templ_data)
|
|
{
|
|
char **config_iter, **template_iter, **templates;
|
|
app_data *data;
|
|
GHashTable *device_templates;
|
|
|
|
templ_data->data->service_list_updates_left = 0;
|
|
|
|
templates = templ_data->templates;
|
|
data = templ_data->data;
|
|
g_slice_free (templates_data, templ_data);
|
|
|
|
if (error) {
|
|
show_main_view (data);
|
|
|
|
g_warning ("Server.GetConfigs() failed: %s", error->message);
|
|
g_strfreev (templates);
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
|
|
device_templates = g_hash_table_new (g_str_hash, g_str_equal);
|
|
|
|
for (template_iter = templates; *template_iter; template_iter++){
|
|
gboolean found_config = FALSE;
|
|
|
|
for (config_iter = configs; *config_iter; config_iter++) {
|
|
if (*template_iter && *config_iter &&
|
|
g_ascii_strncasecmp (*template_iter,
|
|
*config_iter,
|
|
strlen (*config_iter)) == 0) {
|
|
/* have template and config */
|
|
get_config_for_config_widget (data, *config_iter,
|
|
TRUE, TRUE, device_templates);
|
|
found_config = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!found_config) {
|
|
/* have template, no config */
|
|
get_config_for_config_widget (data, *template_iter,
|
|
TRUE, FALSE, device_templates);
|
|
}
|
|
}
|
|
|
|
for (config_iter = configs; *config_iter; config_iter++) {
|
|
gboolean found_template = FALSE;
|
|
|
|
for (template_iter = templates; *template_iter; template_iter++) {
|
|
if (*template_iter &&
|
|
*config_iter &&
|
|
g_ascii_strncasecmp (*template_iter,
|
|
*config_iter,
|
|
strlen (*config_iter)) == 0) {
|
|
|
|
found_template = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!found_template) {
|
|
/* have config, no template */
|
|
get_config_for_config_widget (data, *config_iter,
|
|
FALSE, TRUE, device_templates);
|
|
}
|
|
}
|
|
|
|
/* config initialization might ref/unref as well... */
|
|
g_hash_table_unref (device_templates);
|
|
g_strfreev (configs);
|
|
g_strfreev (templates);
|
|
}
|
|
|
|
static void
|
|
get_template_configs_cb (SyncevoServer *server,
|
|
char **templates,
|
|
GError *error,
|
|
app_data *data)
|
|
{
|
|
templates_data *templ_data;
|
|
|
|
if (error) {
|
|
data->service_list_updates_left = 0;
|
|
show_main_view (data);
|
|
|
|
show_error_dialog (data->sync_win,
|
|
_("Failed to get list of supported services from SyncEvolution"));
|
|
g_warning ("Server.GetConfigs() failed: %s", error->message);
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
|
|
templ_data = g_slice_new (templates_data);
|
|
templ_data->data = data;
|
|
templ_data->templates = templates;
|
|
|
|
syncevo_server_get_configs (data->server,
|
|
FALSE,
|
|
(SyncevoServerGetConfigsCb)get_configs_cb,
|
|
templ_data);
|
|
}
|
|
|
|
static void
|
|
update_services_list (app_data *data)
|
|
{
|
|
if (data->service_list_updates_left > 0) {
|
|
return;
|
|
}
|
|
|
|
gtk_container_foreach (GTK_CONTAINER (data->services_box),
|
|
(GtkCallback)remove_child,
|
|
data->services_box);
|
|
gtk_container_foreach (GTK_CONTAINER (data->devices_box),
|
|
(GtkCallback)remove_child,
|
|
data->devices_box);
|
|
|
|
/* set temp number before we know the real one */
|
|
data->service_list_updates_left = 1;
|
|
syncevo_server_get_configs (data->server,
|
|
TRUE,
|
|
(SyncevoServerGetConfigsCb)get_template_configs_cb,
|
|
data);
|
|
}
|
|
|
|
static void
|
|
get_config_for_main_win_cb (SyncevoServer *server,
|
|
SyncevoConfig *config,
|
|
GError *error,
|
|
app_data *data)
|
|
{
|
|
if (error) {
|
|
if (error->code == DBUS_GERROR_REMOTE_EXCEPTION &&
|
|
dbus_g_error_has_name (error, SYNCEVO_DBUS_ERROR_NO_SUCH_CONFIG)) {
|
|
/* another syncevolution client probably removed the config */
|
|
reload_config (data, NULL);
|
|
} else {
|
|
g_warning ("Error in Server.GetConfig: %s", error->message);
|
|
/* TRANSLATORS: message in main view */
|
|
set_info_bar (data->info_bar, GTK_MESSAGE_ERROR,
|
|
SYNC_ERROR_RESPONSE_NONE,
|
|
_("There was a problem communicating with the "
|
|
"sync process. Please try again later."));
|
|
set_app_state (data, SYNC_UI_STATE_SERVER_FAILURE);
|
|
}
|
|
g_error_free (error);
|
|
|
|
return;
|
|
}
|
|
|
|
if (config) {
|
|
GHashTableIter iter;
|
|
char *name;
|
|
source_config *source;
|
|
|
|
server_config_init (data->current_service, config);
|
|
set_app_state (data, SYNC_UI_STATE_SERVER_OK);
|
|
|
|
/* get "locally supported" status for all sources */
|
|
g_hash_table_iter_init (&iter, data->current_service->source_configs);
|
|
while (g_hash_table_iter_next (&iter, (gpointer)&name, (gpointer)&source)) {
|
|
|
|
syncevo_server_check_source (data->server,
|
|
data->current_service->name,
|
|
name,
|
|
(SyncevoServerGenericCb)check_source_cb,
|
|
source);
|
|
}
|
|
|
|
syncevo_server_get_presence (server,
|
|
data->current_service->name,
|
|
(SyncevoServerGetPresenceCb)get_presence_cb,
|
|
data);
|
|
|
|
syncevo_server_get_reports (server,
|
|
data->current_service->name,
|
|
0, 1,
|
|
(SyncevoServerGetReportsCb)get_reports_cb,
|
|
data);
|
|
|
|
update_service_ui (data);
|
|
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_running_session_status (app_data *data,
|
|
SyncevoSessionStatus status,
|
|
int error_code)
|
|
{
|
|
if (status & SYNCEVO_STATUS_QUEUEING) {
|
|
g_warning ("Running session is queued, this shouldn't happen...");
|
|
} else if (status & SYNCEVO_STATUS_IDLE) {
|
|
set_app_state (data, SYNC_UI_STATE_SERVER_OK);
|
|
} else if (status & SYNCEVO_STATUS_DONE) {
|
|
char *err;
|
|
err = get_error_string_for_code (error_code, NULL);
|
|
if (err) {
|
|
if (data->current_operation == OP_RESTORE) {
|
|
gtk_label_set_text (GTK_LABEL (data->sync_status_label),
|
|
_("Restore failed"));
|
|
} else {
|
|
gtk_label_set_text (GTK_LABEL (data->sync_status_label),
|
|
_("Sync failed"));
|
|
}
|
|
g_free (err);
|
|
} else {
|
|
if (data->current_operation == OP_RESTORE) {
|
|
gtk_label_set_text (GTK_LABEL (data->sync_status_label),
|
|
_("Restore complete"));
|
|
} else {
|
|
gtk_label_set_text (GTK_LABEL (data->sync_status_label),
|
|
_("Sync complete"));
|
|
}
|
|
}
|
|
set_app_state (data, SYNC_UI_STATE_SERVER_OK);
|
|
set_sync_progress (data, 1.0, "");
|
|
} else if (status & SYNCEVO_STATUS_RUNNING ||
|
|
status & SYNCEVO_STATUS_SUSPENDING ||
|
|
status & SYNCEVO_STATUS_ABORTING) {
|
|
set_app_state (data, SYNC_UI_STATE_SYNCING);
|
|
}
|
|
|
|
if (status & SYNCEVO_STATUS_WAITING) {
|
|
gtk_widget_show (data->spinner_image);
|
|
} else {
|
|
gtk_widget_hide (data->spinner_image);
|
|
}
|
|
}
|
|
|
|
static void
|
|
running_session_status_changed_cb (SyncevoSession *session,
|
|
SyncevoSessionStatus status,
|
|
guint error_code,
|
|
SyncevoSourceStatuses *source_statuses,
|
|
app_data *data)
|
|
{
|
|
set_running_session_status (data, status, error_code);
|
|
}
|
|
|
|
static void
|
|
get_running_session_status_cb (SyncevoSession *session,
|
|
SyncevoSessionStatus status,
|
|
guint error_code,
|
|
SyncevoSourceStatuses *source_statuses,
|
|
GError *error,
|
|
app_data *data)
|
|
{
|
|
if (error) {
|
|
g_warning ("Error in Session.GetStatus: %s", error->message);
|
|
g_error_free (error);
|
|
/* non-fatal, unknown error */
|
|
return;
|
|
}
|
|
|
|
set_running_session_status (data, status, error_code);
|
|
}
|
|
|
|
typedef struct source_progress_data {
|
|
app_data *data;
|
|
SyncevoSourcePhase phase;
|
|
const char *source;
|
|
} source_progress_data;
|
|
|
|
static void
|
|
find_updated_source_progress (const char *name,
|
|
SyncevoSourcePhase phase,
|
|
source_progress_data *prog_data)
|
|
{
|
|
GHashTable *configs = prog_data->data->current_service ? prog_data->data->current_service->source_configs : NULL;
|
|
source_config *config;
|
|
config = configs ? g_hash_table_lookup (configs, name) : NULL;
|
|
if (config) {
|
|
if (phase != config->phase) {
|
|
config->phase = phase;
|
|
prog_data->phase = config->phase;
|
|
prog_data->source = name;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
running_session_progress_changed_cb (SyncevoSession *session,
|
|
int progress,
|
|
SyncevoSourceProgresses *source_progresses,
|
|
app_data *data)
|
|
{
|
|
source_progress_data *prog_data = g_slice_new0 (source_progress_data);
|
|
prog_data->data = data;
|
|
prog_data->phase = SYNCEVO_PHASE_NONE;
|
|
prog_data->source = NULL;
|
|
|
|
syncevo_source_progresses_foreach (source_progresses,
|
|
(SourceProgressFunc)find_updated_source_progress,
|
|
prog_data);
|
|
if (!prog_data->source) {
|
|
set_sync_progress (data, ((float)progress) / 100, NULL);
|
|
} else {
|
|
char *name;
|
|
char *msg = NULL;
|
|
|
|
name = get_pretty_source_name (prog_data->source);
|
|
switch (prog_data->phase) {
|
|
case SYNCEVO_PHASE_PREPARING:
|
|
msg = g_strdup_printf (_("Preparing '%s'"), name);
|
|
break;
|
|
case SYNCEVO_PHASE_RECEIVING:
|
|
msg = g_strdup_printf (_("Receiving '%s'"), name);
|
|
break;
|
|
case SYNCEVO_PHASE_SENDING:
|
|
msg = g_strdup_printf (_("Sending '%s'"), name);
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
if (msg) {
|
|
set_sync_progress (data, ((float)progress) / 100, msg);
|
|
}
|
|
g_free (msg);
|
|
g_free (name);
|
|
|
|
}
|
|
|
|
g_slice_free (source_progress_data, prog_data);
|
|
}
|
|
|
|
typedef struct source_stats {
|
|
long status;
|
|
long mode;
|
|
|
|
long local_changes;
|
|
long remote_changes;
|
|
long local_rejections;
|
|
long remote_rejections;
|
|
} source_stats;
|
|
|
|
static void
|
|
free_source_stats (source_stats *stats)
|
|
{
|
|
g_slice_free (source_stats, stats);
|
|
}
|
|
|
|
static gboolean
|
|
handle_source_report_item (char **strs, const char *value, GHashTable *sources)
|
|
{
|
|
source_stats *stats;
|
|
char **tmp;
|
|
char *name;
|
|
|
|
if (g_strv_length (strs) < 3) {
|
|
return FALSE;
|
|
}
|
|
|
|
/* replace '__' with '_' and '_+' with '-' */
|
|
tmp = g_strsplit (strs[1], "__", 0);
|
|
name = g_strjoinv ("_", tmp);
|
|
g_strfreev (tmp);
|
|
tmp = g_strsplit (name, "_+", 0);
|
|
g_free (name);
|
|
name = g_strjoinv ("-", tmp);
|
|
g_strfreev (tmp);
|
|
|
|
stats = g_hash_table_lookup (sources, name);
|
|
if (!stats) {
|
|
stats = g_slice_new0 (source_stats);
|
|
g_hash_table_insert (sources, g_strdup (name), stats);
|
|
}
|
|
g_free (name);
|
|
|
|
if (strcmp (strs[2], "stat") == 0) {
|
|
if (g_strv_length (strs) != 6) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (strcmp (strs[3], "remote") == 0) {
|
|
if (strcmp (strs[4], "added") == 0 ||
|
|
strcmp (strs[4], "updated") == 0 ||
|
|
strcmp (strs[4], "removed") == 0) {
|
|
stats->remote_changes += strtol (value, NULL, 10);
|
|
} else if (strcmp (strs[5], "reject") == 0) {
|
|
stats->remote_rejections += strtol (value, NULL, 10);
|
|
}
|
|
|
|
} else if (strcmp (strs[3], "local") == 0) {
|
|
if (strcmp (strs[4], "added") == 0 ||
|
|
strcmp (strs[4], "updated") == 0 ||
|
|
strcmp (strs[4], "removed") == 0) {
|
|
stats->local_changes += strtol (value, NULL, 10);
|
|
} else if (strcmp (strs[5], "reject") == 0) {
|
|
stats->local_rejections += strtol (value, NULL, 10);
|
|
}
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else if (strcmp (strs[2], "mode") == 0) {
|
|
stats->mode = strtol (value, NULL, 10);
|
|
} else if (strcmp (strs[2], "status") == 0) {
|
|
stats->status = strtol (value, NULL, 10);
|
|
} else if (strcmp (strs[2], "resume") == 0) {
|
|
} else if (strcmp (strs[2], "first") == 0) {
|
|
} else if (strcmp (strs[2], "backup") == 0) {
|
|
if (g_strv_length (strs) != 4) {
|
|
return FALSE;
|
|
}
|
|
if (strcmp (strs[3], "before") == 0) {
|
|
} else if (strcmp (strs[3], "after") == 0) {
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static char*
|
|
get_report_summary (source_config *source)
|
|
{
|
|
char *rejects = NULL;
|
|
char *changes = NULL;
|
|
char *msg = NULL;
|
|
|
|
if (!source->stats_set) {
|
|
return g_strdup ("");
|
|
}
|
|
|
|
if (source->local_rejections + source->remote_rejections == 0) {
|
|
rejects = NULL;
|
|
} else if (source->local_rejections == 0) {
|
|
rejects = g_strdup_printf (ngettext ("There was one remote rejection.",
|
|
"There were %ld remote rejections.",
|
|
source->remote_rejections),
|
|
source->remote_rejections);
|
|
} else if (source->remote_rejections == 0) {
|
|
rejects = g_strdup_printf (ngettext ("There was one local rejection.",
|
|
"There were %ld local rejections.",
|
|
source->local_rejections),
|
|
source->local_rejections);
|
|
} else {
|
|
rejects = g_strdup_printf (_ ("There were %ld local rejections and %ld remote rejections."),
|
|
source->local_rejections, source->remote_rejections);
|
|
}
|
|
|
|
if (source->local_changes + source->remote_changes == 0) {
|
|
changes = g_strdup_printf (_("Last time: No changes."));
|
|
} else if (source->local_changes == 0) {
|
|
changes = g_strdup_printf (ngettext ("Last time: Sent one change.",
|
|
"Last time: Sent %ld changes.",
|
|
source->remote_changes),
|
|
source->remote_changes);
|
|
} else if (source->remote_changes == 0) {
|
|
// This is about changes made to the local data. Not all of these
|
|
// changes were requested by the remote server, so "applied"
|
|
// is a better word than "received" (bug #5185).
|
|
changes = g_strdup_printf (ngettext ("Last time: Applied one change.",
|
|
"Last time: Applied %ld changes.",
|
|
source->local_changes),
|
|
source->local_changes);
|
|
} else {
|
|
changes = g_strdup_printf (_("Last time: Applied %ld changes and sent %ld changes."),
|
|
source->local_changes, source->remote_changes);
|
|
}
|
|
|
|
if (rejects)
|
|
msg = g_strdup_printf ("%s\n%s", changes, rejects);
|
|
else
|
|
msg = g_strdup (changes);
|
|
g_free (rejects);
|
|
g_free (changes);
|
|
return msg;
|
|
}
|
|
|
|
/* return TRUE if no errors are shown */
|
|
static gboolean
|
|
source_config_update_widget (source_config *source)
|
|
{
|
|
char *msg;
|
|
gboolean show_error;
|
|
SyncErrorResponse response;
|
|
|
|
if (!source->label) {
|
|
return TRUE;
|
|
}
|
|
|
|
msg = get_error_string_for_code (source->status, &response);
|
|
if (msg) {
|
|
show_error = TRUE;
|
|
set_info_bar (source->info_bar, GTK_MESSAGE_ERROR, response, msg);
|
|
} else {
|
|
show_error = FALSE;
|
|
gtk_widget_hide (source->info_bar);
|
|
msg = get_report_summary (source);
|
|
gtk_label_set_text (GTK_LABEL (source->label), msg);
|
|
}
|
|
g_free (msg);
|
|
|
|
return !show_error;
|
|
}
|
|
|
|
|
|
static void
|
|
get_reports_cb (SyncevoServer *server,
|
|
SyncevoReports *reports,
|
|
GError *error,
|
|
app_data *data)
|
|
{
|
|
long status = -1;
|
|
long common_status = -1;
|
|
source_stats *stats;
|
|
GHashTable *sources; /* key is source name, value is a source_stats */
|
|
GHashTableIter iter;
|
|
const char *key, *val;
|
|
source_config *source_conf;
|
|
char *error_msg;
|
|
SyncErrorResponse response;
|
|
gboolean have_source_errors;
|
|
GHashTable *report = NULL;
|
|
guint len;
|
|
|
|
if (error) {
|
|
g_warning ("Error in Session.GetReports: %s", error->message);
|
|
g_error_free (error);
|
|
/* non-fatal, unknown error */
|
|
return;
|
|
}
|
|
|
|
sources = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
g_free, (GDestroyNotify)free_source_stats);
|
|
|
|
len = syncevo_reports_get_length (reports);
|
|
|
|
if (len > 0) {
|
|
report = syncevo_reports_index (reports, 0);
|
|
val = g_hash_table_lookup (report, "dir");
|
|
if (!val || strlen (val) == 0) {
|
|
/* dummy report for first time sync info*/
|
|
if (len > 1) {
|
|
report = syncevo_reports_index (reports, 1);
|
|
} else {
|
|
report = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (report) {
|
|
g_hash_table_iter_init (&iter, report);
|
|
while (g_hash_table_iter_next (&iter, (gpointer)&key, (gpointer)&val)) {
|
|
char **strs;
|
|
strs = g_strsplit (key, "-", 6);
|
|
if (!strs) {
|
|
continue;
|
|
}
|
|
|
|
if (strcmp (strs[0], "source") == 0) {
|
|
if (!handle_source_report_item (strs, val, sources)) {
|
|
g_warning ("Unidentified sync report item: %s=%s",
|
|
key, val);
|
|
}
|
|
} else if (strcmp (strs[0], "start") == 0) {
|
|
/* not used */
|
|
} else if (strcmp (strs[0], "end") == 0) {
|
|
data->last_sync = strtol (val, NULL, 10);
|
|
} else if (strcmp (strs[0], "status") == 0) {
|
|
status = strtol (val, NULL, 10);
|
|
} else if (strcmp (strs[0], "peer") == 0) {
|
|
/* not used */
|
|
} else if (strcmp (strs[0], "error") == 0) {
|
|
/* not used */
|
|
} else if (strcmp (strs[0], "dir") == 0) {
|
|
/* not used */
|
|
} else {
|
|
g_warning ("Unidentified sync report item: %s=%s",
|
|
key, val);
|
|
}
|
|
|
|
g_strfreev (strs);
|
|
|
|
}
|
|
} else {
|
|
common_status = 0;
|
|
}
|
|
|
|
/* sources now has all statistics we want */
|
|
|
|
/* ficure out if all sources have same status or if there's a slow sync */
|
|
data->forced_emergency = FALSE;
|
|
g_hash_table_remove_all (data->emergency_sources);
|
|
|
|
g_hash_table_iter_init (&iter, sources);
|
|
while (g_hash_table_iter_next (&iter, (gpointer)&key, (gpointer)&stats)) {
|
|
if (stats->status == 22001) {
|
|
/* ignore abort because of another source slow syncing */
|
|
} else if (stats->status == 22000) {
|
|
common_status = stats->status;
|
|
data->forced_emergency = TRUE;
|
|
g_hash_table_insert (data->emergency_sources,
|
|
g_strdup (key), "");
|
|
} else if (common_status == -1) {
|
|
common_status = stats->status;
|
|
} else if (common_status != stats->status) {
|
|
common_status = 0;
|
|
}
|
|
}
|
|
|
|
if (status != 200) {
|
|
/* don't want to show a sync time for failed syncs */
|
|
data->last_sync = -1;
|
|
}
|
|
|
|
if (!data->forced_emergency) {
|
|
/* if user initiates a emergency sync wihtout forced_emergency,
|
|
enable all sources by default*/
|
|
g_hash_table_iter_init (&iter, data->current_service->source_configs);
|
|
while (g_hash_table_iter_next (&iter, (gpointer)&key, (gpointer)&source_conf)) {
|
|
if (source_config_is_usable (source_conf)) {
|
|
g_hash_table_insert (data->emergency_sources, g_strdup (key), "");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* get common error message */
|
|
error_msg = get_error_string_for_code (common_status, &response);
|
|
have_source_errors = FALSE;
|
|
|
|
g_hash_table_iter_init (&iter, sources);
|
|
while (g_hash_table_iter_next (&iter, (gpointer)&key, (gpointer)&stats)) {
|
|
/* store the statistics in source config */
|
|
source_conf = g_hash_table_lookup (data->current_service->source_configs,
|
|
key);
|
|
if (source_conf) {
|
|
source_conf->stats_set = TRUE;
|
|
source_conf->local_changes = stats->local_changes;
|
|
source_conf->remote_changes = stats->remote_changes;
|
|
source_conf->local_rejections = stats->local_rejections;
|
|
source_conf->remote_rejections = stats->remote_rejections;
|
|
if (error_msg) {
|
|
/* there is a service-wide error, no need to show here */
|
|
source_conf->status = 0;
|
|
} else {
|
|
source_conf->status = stats->status;
|
|
}
|
|
/* if ui has been constructed already, update it */
|
|
if (!source_config_update_widget (source_conf)) {
|
|
have_source_errors = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!error_msg && !have_source_errors) {
|
|
/* no common source errors or individual source errors:
|
|
it's still possible that there are sync errors */
|
|
error_msg = get_error_string_for_code (status, &response);
|
|
}
|
|
|
|
/* update service UI */
|
|
refresh_last_synced_label (data);
|
|
if (error_msg) {
|
|
GtkMessageType type = GTK_MESSAGE_ERROR;
|
|
|
|
if (response == SYNC_ERROR_RESPONSE_EMERGENCY) {
|
|
type = GTK_MESSAGE_QUESTION;
|
|
}
|
|
|
|
if (!data->synced_this_session) {
|
|
/* TRANSLATORS: the placeholder is a error message (hopefully)
|
|
* explaining the problem */
|
|
char *msg = g_strdup_printf (_("There was a problem with last sync:\n%s"),
|
|
error_msg);
|
|
g_free (error_msg);
|
|
error_msg = msg;
|
|
}
|
|
set_info_bar (data->info_bar, type, response, error_msg);
|
|
g_free (error_msg);
|
|
} else if (data->current_operation == OP_RESTORE) {
|
|
/* special case for just after restoring */
|
|
error_msg = g_strdup_printf
|
|
(_("You've just restored a backup. The changes have not been "
|
|
"synced with %s yet"), data->current_service->pretty_name);
|
|
set_info_bar (data->info_bar,
|
|
GTK_MESSAGE_INFO,
|
|
SYNC_ERROR_RESPONSE_SYNC,
|
|
error_msg);
|
|
}
|
|
|
|
g_hash_table_destroy (sources);
|
|
}
|
|
|
|
static void
|
|
set_config_cb (SyncevoSession *session,
|
|
GError *error,
|
|
app_data *data)
|
|
{
|
|
if (error) {
|
|
g_warning ("Error in Session.SetConfig: %s", error->message);
|
|
g_error_free (error);
|
|
/* TODO show in UI: save failed */
|
|
}
|
|
g_object_unref (session);
|
|
}
|
|
|
|
static void
|
|
restore_cb (SyncevoSession *session,
|
|
GError *error,
|
|
app_data *data)
|
|
{
|
|
if (error) {
|
|
g_warning ("Error in Session.Restore: %s", error->message);
|
|
g_error_free (error);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
restore_backup (app_data *data, SyncevoSession *session, const char *dir)
|
|
{
|
|
char **sources;
|
|
GHashTableIter iter;
|
|
int i = 0;
|
|
char *source;
|
|
|
|
sources = g_malloc0 (sizeof (char*) *
|
|
(g_hash_table_size (data->emergency_sources) + 1));
|
|
|
|
g_hash_table_iter_init (&iter, data->emergency_sources);
|
|
while (g_hash_table_iter_next (&iter, (gpointer)&source, NULL)) {
|
|
sources[i++] = g_strdup (source);
|
|
}
|
|
sources[i] = NULL;
|
|
|
|
syncevo_session_restore (session, dir, TRUE, (const char**)sources,
|
|
(SyncevoSessionGenericCb)restore_cb,
|
|
data);
|
|
|
|
g_strfreev (sources);
|
|
}
|
|
|
|
static void
|
|
save_config (app_data *data, SyncevoSession *session)
|
|
{
|
|
syncevo_session_set_config (session,
|
|
TRUE,
|
|
FALSE,
|
|
data->current_service->config,
|
|
(SyncevoSessionGenericCb)set_config_cb,
|
|
data);
|
|
}
|
|
|
|
static void
|
|
do_sync (operation_data *op_data, SyncevoSession *session)
|
|
{
|
|
GHashTable *source_modes;
|
|
GHashTableIter iter;
|
|
source_config *source;
|
|
SyncevoSyncMode mode = SYNCEVO_SYNC_NONE;
|
|
|
|
app_data *data = op_data->data;
|
|
|
|
source_modes = syncevo_source_modes_new ();
|
|
|
|
if (op_data->operation != OP_SYNC) {
|
|
/* in an emergency sync, set non-emergency sources to not sync*/
|
|
g_hash_table_iter_init (&iter, data->current_service->source_configs);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer)&source)) {
|
|
if (g_hash_table_lookup (data->emergency_sources, source->name) == NULL) {
|
|
syncevo_source_modes_add (source_modes,
|
|
source->name,
|
|
SYNCEVO_SYNC_NONE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* override all non-supported with "none". */
|
|
g_hash_table_iter_init (&iter, data->current_service->source_configs);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer)&source)) {
|
|
if (!source->supported_locally) {
|
|
syncevo_source_modes_add (source_modes,
|
|
source->name,
|
|
SYNCEVO_SYNC_NONE);
|
|
}
|
|
}
|
|
|
|
/* use given mode or use default for normal syncs */
|
|
switch (op_data->operation) {
|
|
case OP_SYNC:
|
|
mode = SYNCEVO_SYNC_DEFAULT;
|
|
break;
|
|
case OP_SYNC_SLOW:
|
|
mode = SYNCEVO_SYNC_SLOW;
|
|
break;
|
|
case OP_SYNC_REFRESH_FROM_CLIENT:
|
|
mode = SYNCEVO_SYNC_REFRESH_FROM_CLIENT;
|
|
break;
|
|
case OP_SYNC_REFRESH_FROM_SERVER:
|
|
mode = SYNCEVO_SYNC_REFRESH_FROM_SERVER;
|
|
break;
|
|
default:
|
|
g_warn_if_reached();
|
|
}
|
|
syncevo_session_sync (session,
|
|
mode,
|
|
source_modes,
|
|
(SyncevoSessionGenericCb)sync_cb,
|
|
data);
|
|
syncevo_source_modes_free (source_modes);
|
|
}
|
|
|
|
static void
|
|
set_config_for_sync_cb (SyncevoSession *session,
|
|
GError *error,
|
|
operation_data *op_data)
|
|
{
|
|
if (error) {
|
|
g_warning ("Error in Session.SetConfig: %s", error->message);
|
|
g_error_free (error);
|
|
/* TODO show in UI: sync failed (failed to even start) */
|
|
return;
|
|
}
|
|
|
|
do_sync (op_data, session);
|
|
}
|
|
|
|
static void
|
|
run_operation (operation_data *op_data, SyncevoSession *session)
|
|
{
|
|
SyncevoConfig *config;
|
|
|
|
/* when we first get idle, start the operation */
|
|
if (op_data->started) {
|
|
return;
|
|
}
|
|
op_data->started = TRUE;
|
|
op_data->data->current_operation = op_data->operation;
|
|
|
|
/* time for business */
|
|
switch (op_data->operation) {
|
|
case OP_SYNC:
|
|
case OP_SYNC_SLOW:
|
|
case OP_SYNC_REFRESH_FROM_CLIENT:
|
|
case OP_SYNC_REFRESH_FROM_SERVER:
|
|
/* Make sure we don't get change diffs printed out, then sync */
|
|
config = g_hash_table_new (g_str_hash, g_str_equal);
|
|
syncevo_config_set_value (config,
|
|
NULL, "printChanges", "0");
|
|
syncevo_session_set_config (session,
|
|
TRUE,
|
|
TRUE,
|
|
config,
|
|
(SyncevoSessionGenericCb)set_config_for_sync_cb,
|
|
op_data);
|
|
syncevo_config_free (config);
|
|
|
|
break;
|
|
case OP_SAVE:
|
|
save_config (op_data->data, session);
|
|
break;
|
|
case OP_RESTORE:
|
|
restore_backup (op_data->data, session, op_data->dir);
|
|
break;
|
|
default:
|
|
g_warn_if_reached ();
|
|
}
|
|
}
|
|
/* Our sync session status */
|
|
static void
|
|
status_changed_cb (SyncevoSession *session,
|
|
SyncevoSessionStatus status,
|
|
guint error_code,
|
|
SyncevoSourceStatuses *source_statuses,
|
|
operation_data *op_data)
|
|
{
|
|
|
|
switch (status) {
|
|
case SYNCEVO_STATUS_IDLE:
|
|
run_operation (op_data, session);
|
|
break;
|
|
case SYNCEVO_STATUS_DONE:
|
|
op_data->data->synced_this_session = TRUE;
|
|
|
|
/* no need for sync session anymore */
|
|
g_object_unref (session);
|
|
|
|
/* refresh stats -- the service may no longer be the one syncing,
|
|
* and we might have only saved config but what the heck... */
|
|
syncevo_server_get_reports (op_data->data->server,
|
|
op_data->data->current_service->name,
|
|
0, 1,
|
|
(SyncevoServerGetReportsCb)get_reports_cb,
|
|
op_data->data);
|
|
|
|
g_slice_free (operation_data, op_data);
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
/* Our sync (or config-save) session status */
|
|
static void
|
|
get_status_cb (SyncevoSession *session,
|
|
SyncevoSessionStatus status,
|
|
guint error_code,
|
|
SyncevoSourceStatuses *source_statuses,
|
|
GError *error,
|
|
operation_data *op_data)
|
|
{
|
|
if (error) {
|
|
g_warning ("Error in Session.GetStatus: %s", error->message);
|
|
g_error_free (error);
|
|
g_object_unref (session);
|
|
|
|
switch (op_data->operation) {
|
|
case OP_SYNC:
|
|
/* TODO show in UI: sync failed (failed to even start) */
|
|
break;
|
|
case OP_SAVE:
|
|
/* TODO show in UI: save failed */
|
|
break;
|
|
default:
|
|
g_warn_if_reached ();
|
|
}
|
|
g_slice_free (operation_data, op_data);
|
|
return;
|
|
}
|
|
|
|
if (status == SYNCEVO_STATUS_IDLE) {
|
|
run_operation (op_data, session);
|
|
}
|
|
}
|
|
|
|
static void
|
|
start_session_cb (SyncevoServer *server,
|
|
char *path,
|
|
GError *error,
|
|
operation_data *op_data)
|
|
{
|
|
SyncevoSession *session;
|
|
app_data *data = op_data->data;
|
|
|
|
if (error) {
|
|
g_warning ("Error in Server.StartSession: %s", error->message);
|
|
g_error_free (error);
|
|
g_free (path);
|
|
|
|
switch (op_data->operation) {
|
|
case OP_SYNC:
|
|
/* TODO show in UI: sync failed (failed to even start) */
|
|
break;
|
|
case OP_SAVE:
|
|
/* TODO show in UI: save failed */
|
|
break;
|
|
default:
|
|
g_warn_if_reached ();
|
|
}
|
|
g_slice_free (operation_data, op_data);
|
|
return;
|
|
}
|
|
|
|
session = syncevo_session_new (path);
|
|
|
|
if (data->running_session &&
|
|
strcmp (path, syncevo_session_get_path (data->running_session)) != 0) {
|
|
/* This is a really unfortunate event:
|
|
Someone got a session and we did not have time to set UI insensitive... */
|
|
gtk_label_set_markup (GTK_LABEL (data->server_label),
|
|
_("Waiting for current operation to finish..."));
|
|
gtk_widget_show_all (data->sources_box);
|
|
}
|
|
|
|
/* we want to know about status changes to our session */
|
|
g_signal_connect (session, "status-changed",
|
|
G_CALLBACK (status_changed_cb), op_data);
|
|
syncevo_session_get_status (session,
|
|
(SyncevoSessionGetStatusCb)get_status_cb,
|
|
op_data);
|
|
|
|
g_free (path);
|
|
}
|
|
|
|
/* TODO: this function should accept source/peer name as param */
|
|
char*
|
|
get_error_string_for_code (int error_code, SyncErrorResponse *response)
|
|
{
|
|
if (response) {
|
|
*response = SYNC_ERROR_RESPONSE_NONE;
|
|
}
|
|
|
|
switch (error_code) {
|
|
case -1: /* no errorcode */
|
|
case 0:
|
|
case 200:
|
|
case LOCERR_USERABORT:
|
|
case LOCERR_USERSUSPEND:
|
|
return NULL;
|
|
case 22000:
|
|
if (response) {
|
|
*response = SYNC_ERROR_RESPONSE_EMERGENCY;
|
|
}
|
|
/* TRANSLATORS: next strings are error messages. */
|
|
return g_strdup (_("A normal sync is not possible at this time. The server "
|
|
"suggests a slow sync, but this might not always be "
|
|
"what you want if both ends already have data."));
|
|
case 22002:
|
|
return g_strdup (_("The sync process died unexpectedly."));
|
|
case 22003:
|
|
if (response) {
|
|
*response = SYNC_ERROR_RESPONSE_SETTINGS_OPEN;
|
|
}
|
|
return g_strdup (_("Password request was not answered. You can save the "
|
|
"password in the settings to prevent the request."));
|
|
case 506:
|
|
/* TODO use the service device name here, this is a remote problem */
|
|
return g_strdup (_("There was a problem processing sync request. "
|
|
"Trying again may help."));
|
|
case DB_Unauthorized:
|
|
if (response) {
|
|
*response = SYNC_ERROR_RESPONSE_SETTINGS_OPEN;
|
|
}
|
|
return g_strdup(_("Failed to login. Could there be a problem with "
|
|
"your username or password?"));
|
|
case DB_Forbidden:
|
|
return g_strdup(_("Forbidden"));
|
|
case DB_NotFound:
|
|
if (response) {
|
|
*response = SYNC_ERROR_RESPONSE_SETTINGS_OPEN;
|
|
}
|
|
/* TRANSLATORS: data source means e.g. calendar or addressbook */
|
|
return g_strdup(_("A data source could not be found. Could there be a "
|
|
"problem with the settings?"));
|
|
case DB_Fatal:
|
|
case DB_Error:
|
|
return g_strdup(_("Remote database error"));
|
|
case LOCAL_STATUS_CODE + DB_Fatal:
|
|
/* This can happen when EDS is borked, restart it may help... */
|
|
return g_strdup(_("There is a problem with the local database. "
|
|
"Syncing again or rebooting may help."));
|
|
case DB_Full:
|
|
return g_strdup(_("No space on disk"));
|
|
case LOCERR_PROCESSMSG:
|
|
return g_strdup(_("Failed to process SyncML"));
|
|
case LOCERR_AUTHFAIL:
|
|
return g_strdup(_("Server authorization failed"));
|
|
case LOCERR_CFGPARSE:
|
|
return g_strdup(_("Failed to parse configuration file"));
|
|
case LOCERR_CFGREAD:
|
|
return g_strdup(_("Failed to read configuration file"));
|
|
case LOCERR_NOCFG:
|
|
return g_strdup(_("No configuration found"));
|
|
case LOCERR_NOCFGFILE:
|
|
return g_strdup(_("No configuration file found"));
|
|
case LOCERR_BADCONTENT:
|
|
return g_strdup(_("Server sent bad content"));
|
|
case LOCERR_CERT_EXPIRED:
|
|
return g_strdup(_("Connection certificate has expired"));
|
|
case LOCERR_CERT_INVALID:
|
|
return g_strdup(_("Connection certificate is invalid"));
|
|
case LOCERR_CONN:
|
|
case LOCERR_NOCONN:
|
|
case LOCERR_TRANSPFAIL:
|
|
case LOCERR_TIMEOUT:
|
|
if (response) {
|
|
*response = SYNC_ERROR_RESPONSE_SETTINGS_OPEN;
|
|
}
|
|
return g_strdup(_("We were unable to connect to the server. The problem "
|
|
"could be temporary or there could be something wrong "
|
|
"with the settings."));
|
|
case LOCERR_BADURL:
|
|
if (response) {
|
|
*response = SYNC_ERROR_RESPONSE_SETTINGS_OPEN;
|
|
}
|
|
return g_strdup(_("The server URL is bad"));
|
|
case LOCERR_SRVNOTFOUND:
|
|
if (response) {
|
|
*response = SYNC_ERROR_RESPONSE_SETTINGS_OPEN;
|
|
}
|
|
return g_strdup(_("The server was not found"));
|
|
default:
|
|
return g_strdup_printf (_("Error %d"), error_code);
|
|
}
|
|
}
|
|
|
|
static void
|
|
server_shutdown_cb (SyncevoServer *server,
|
|
app_data *data)
|
|
{
|
|
if (data->syncing) {
|
|
gtk_label_set_text (GTK_LABEL (data->sync_status_label),
|
|
_("Sync failed"));
|
|
set_sync_progress (data, 1.0 , "");
|
|
set_app_state (data, SYNC_UI_STATE_SERVER_OK);
|
|
}
|
|
|
|
/* re-init server here */
|
|
}
|
|
|
|
|
|
static void
|
|
set_running_session (app_data *data, const char *path)
|
|
{
|
|
if (data->running_session) {
|
|
g_object_unref (data->running_session);
|
|
}
|
|
|
|
if (!path) {
|
|
data->running_session = NULL;
|
|
return;
|
|
}
|
|
|
|
data->running_session = syncevo_session_new (path);
|
|
|
|
g_signal_connect (data->running_session, "progress-changed",
|
|
G_CALLBACK (running_session_progress_changed_cb), data);
|
|
g_signal_connect (data->running_session, "status-changed",
|
|
G_CALLBACK (running_session_status_changed_cb), data);
|
|
syncevo_session_get_status (data->running_session,
|
|
(SyncevoSessionGetStatusCb)get_running_session_status_cb,
|
|
data);
|
|
}
|
|
|
|
static void
|
|
set_online_status (app_data *data, gboolean online)
|
|
{
|
|
if (online != data->online) {
|
|
data->online = online;
|
|
|
|
if (data->current_state == SYNC_UI_STATE_SERVER_OK) {
|
|
if (data->online) {
|
|
gtk_widget_hide (data->no_connection_box);
|
|
} else {
|
|
gtk_widget_show (data->no_connection_box);
|
|
}
|
|
}
|
|
gtk_widget_set_sensitive (data->sync_btn, data->online);
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_presence_cb (SyncevoServer *server,
|
|
char *status,
|
|
char **transports,
|
|
GError *error,
|
|
app_data *data)
|
|
{
|
|
if (error) {
|
|
g_warning ("Server.GetSessions failed: %s", error->message);
|
|
g_error_free (error);
|
|
/* non-fatal, ignore in UI */
|
|
return;
|
|
}
|
|
|
|
if (data->current_service && status) {
|
|
set_online_status (data, strcmp (status, "") == 0);
|
|
}
|
|
g_free (status);
|
|
g_strfreev (transports);
|
|
}
|
|
|
|
static void
|
|
password_dialog_response_cb (GtkWidget *dialog, int response, app_data *data)
|
|
{
|
|
const char *password;
|
|
GHashTable *return_dict;
|
|
|
|
return_dict = g_hash_table_new (g_str_hash, g_str_equal);
|
|
|
|
if (response == GTK_RESPONSE_OK) {
|
|
password = gtk_entry_get_text (GTK_ENTRY (data->password_dialog_entry));
|
|
g_hash_table_insert (return_dict, "password", (gpointer)password);
|
|
}
|
|
|
|
syncevo_server_info_response (data->server, data->password_dialog_id,
|
|
"response", return_dict, NULL, NULL);
|
|
|
|
g_hash_table_destroy (return_dict);
|
|
|
|
g_free (data->password_dialog_id);
|
|
data->password_dialog_id = NULL;
|
|
gtk_widget_destroy (dialog);
|
|
}
|
|
|
|
static void
|
|
info_request_cb (SyncevoServer *syncevo,
|
|
char *id,
|
|
char *session_path,
|
|
char *state,
|
|
char *handler_path,
|
|
char *type,
|
|
GHashTable *parameters,
|
|
app_data *data)
|
|
{
|
|
GHashTable *t;
|
|
GtkWidget *dialog, *content, *label, *align;
|
|
char *msg;
|
|
|
|
if (g_strcmp0 (state, "request") != 0 ||
|
|
g_strcmp0 (type, "password") != 0) {
|
|
/* not handling other stuff */
|
|
return;
|
|
}
|
|
|
|
if (!data->running_session ||
|
|
g_strcmp0 (session_path,
|
|
syncevo_session_get_path (data->running_session)) != 0) {
|
|
/* not our problem */
|
|
return;
|
|
}
|
|
|
|
t = g_hash_table_new (g_str_hash, g_str_equal);
|
|
syncevo_server_info_response (syncevo, id, "working", t, NULL, NULL);
|
|
g_hash_table_destroy (t);
|
|
|
|
data->password_dialog_id = g_strdup (id);
|
|
|
|
/* TRANSLATORS: password request dialog contents: title, cancel button
|
|
* and ok button */
|
|
dialog = gtk_dialog_new_with_buttons (_("Password is required for sync"),
|
|
GTK_WINDOW (data->sync_win),
|
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
_("Cancel sync"), GTK_RESPONSE_CANCEL,
|
|
_("Sync with password"), GTK_RESPONSE_OK,
|
|
NULL);
|
|
content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
|
|
|
|
align = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
|
|
gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 16, 16);
|
|
gtk_widget_show (align);
|
|
gtk_box_pack_start (GTK_BOX (content), align, FALSE, FALSE, 6);
|
|
|
|
/* TRANSLATORS: password request dialog message, placeholder is service name */
|
|
msg = g_strdup_printf (_("Please enter password for syncing with %s:"),
|
|
data->current_service->pretty_name);
|
|
label = gtk_label_new (msg);
|
|
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
|
|
gtk_widget_set_size_request (label, 500, -1);
|
|
|
|
gtk_widget_show (label);
|
|
gtk_container_add (GTK_CONTAINER (align), label);
|
|
g_free (msg);
|
|
|
|
align = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
|
|
gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 16, 16);
|
|
gtk_widget_show (align);
|
|
gtk_box_pack_start (GTK_BOX (content), align, FALSE, FALSE, 6);
|
|
|
|
data->password_dialog_entry = gtk_entry_new ();
|
|
gtk_entry_set_max_length (GTK_ENTRY (data->password_dialog_entry), 99);
|
|
gtk_entry_set_width_chars (GTK_ENTRY (data->password_dialog_entry), 30);
|
|
gtk_entry_set_visibility (GTK_ENTRY (data->password_dialog_entry), FALSE);
|
|
|
|
gtk_widget_show (data->password_dialog_entry);
|
|
gtk_container_add (GTK_CONTAINER (align), data->password_dialog_entry);
|
|
|
|
g_signal_connect (dialog, "response",
|
|
G_CALLBACK (password_dialog_response_cb), data);
|
|
|
|
gtk_window_present (GTK_WINDOW (dialog));
|
|
gtk_widget_grab_focus (data->password_dialog_entry);
|
|
}
|
|
|
|
static void
|
|
server_presence_changed_cb (SyncevoServer *server,
|
|
char *config_name,
|
|
char *status,
|
|
char *transport,
|
|
app_data *data)
|
|
{
|
|
if (data->current_service &&
|
|
config_name && status &&
|
|
g_ascii_strcasecmp (data->current_service->name, config_name) == 0) {
|
|
|
|
set_online_status (data, strcmp (status, "") == 0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
server_templates_changed_cb (SyncevoServer *server,
|
|
app_data *data)
|
|
{
|
|
if (gtk_widget_get_visible (data->services_box)) {
|
|
update_services_list (data);
|
|
}
|
|
}
|
|
|
|
static void
|
|
server_session_changed_cb (SyncevoServer *server,
|
|
char *path,
|
|
gboolean started,
|
|
app_data *data)
|
|
{
|
|
if (started) {
|
|
set_running_session (data, path);
|
|
} else if (data->running_session &&
|
|
strcmp (syncevo_session_get_path (data->running_session), path) == 0 ) {
|
|
set_running_session (data, NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_sessions_cb (SyncevoServer *server,
|
|
SyncevoSessions *sessions,
|
|
GError *error,
|
|
app_data *data)
|
|
{
|
|
const char *path;
|
|
|
|
if (error) {
|
|
g_warning ("Server.GetSessions failed: %s", error->message);
|
|
g_error_free (error);
|
|
|
|
/* TODO show in UI: failed first syncevo call (unexpected, fatal?) */
|
|
return;
|
|
}
|
|
|
|
/* assume first one is active */
|
|
path = syncevo_sessions_index (sessions, 0);
|
|
set_running_session (data, path);
|
|
|
|
syncevo_sessions_free (sessions);
|
|
}
|
|
|
|
static void
|
|
get_config_for_default_peer_cb (SyncevoServer *syncevo,
|
|
SyncevoConfig *config,
|
|
GError *error,
|
|
app_data *data)
|
|
{
|
|
char *name;
|
|
|
|
if (error) {
|
|
g_warning ("Server.GetConfig failed: %s", error->message);
|
|
g_error_free (error);
|
|
/* TODO show in UI: failed first syncevo call (unexpected, fatal?) */
|
|
return;
|
|
}
|
|
|
|
syncevo_config_get_value (config, NULL, "defaultPeer", &name);
|
|
reload_config (data, name);
|
|
|
|
syncevo_config_free (config);
|
|
}
|
|
|
|
app_data*
|
|
sync_ui_create ()
|
|
{
|
|
app_data *data;
|
|
|
|
data = g_slice_new0 (app_data);
|
|
data->online = TRUE;
|
|
data->current_state = SYNC_UI_STATE_GETTING_SERVER;
|
|
data->forced_emergency = FALSE;
|
|
data->emergency_sources = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
g_free, NULL);
|
|
if (!init_ui (data)) {
|
|
return NULL;
|
|
}
|
|
|
|
data->server = syncevo_server_get_default();
|
|
g_signal_connect (data->server, "shutdown",
|
|
G_CALLBACK (server_shutdown_cb), data);
|
|
g_signal_connect (data->server, "session-changed",
|
|
G_CALLBACK (server_session_changed_cb), data);
|
|
g_signal_connect (data->server, "presence-changed",
|
|
G_CALLBACK (server_presence_changed_cb), data);
|
|
g_signal_connect (data->server, "templates-changed",
|
|
G_CALLBACK (server_templates_changed_cb), data);
|
|
g_signal_connect (data->server, "info-request",
|
|
G_CALLBACK (info_request_cb), data);
|
|
|
|
syncevo_server_get_config (data->server,
|
|
"",
|
|
FALSE,
|
|
(SyncevoServerGetConfigCb)get_config_for_default_peer_cb,
|
|
data);
|
|
|
|
syncevo_server_get_sessions (data->server,
|
|
(SyncevoServerGetSessionsCb)get_sessions_cb,
|
|
data);
|
|
|
|
gtk_window_present (GTK_WINDOW (data->sync_win));
|
|
|
|
return data;
|
|
}
|
|
|
|
void sync_ui_show_settings (app_data *data, const char *id)
|
|
{
|
|
show_services_list (data, id);
|
|
}
|
|
|
|
GtkWindow*
|
|
sync_ui_get_main_window (app_data *data)
|
|
{
|
|
return GTK_WINDOW(data->sync_win);
|
|
}
|