[epiphany] Split the Preferences Dialog
- From: Michael Catanzaro <mcatanzaro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany] Split the Preferences Dialog
- Date: Fri, 15 May 2020 16:09:18 +0000 (UTC)
commit 95052545c8c4a4bcfdd57f89e2ed738f5def0252
Author: Yetizone <andreii lisita gmail com>
Date: Fri May 8 14:50:35 2020 +0300
Split the Preferences Dialog
src/meson.build | 19 +-
src/{ => preferences}/clear-data-dialog.c | 0
src/{ => preferences}/clear-data-dialog.h | 0
src/{ => preferences}/cookies-dialog.c | 0
src/{ => preferences}/cookies-dialog.h | 0
src/{ => preferences}/ephy-search-engine-dialog.c | 0
src/{ => preferences}/ephy-search-engine-dialog.h | 0
src/{ => preferences}/passwords-dialog.c | 0
src/{ => preferences}/passwords-dialog.h | 0
src/preferences/prefs-appearance-page.c | 426 ++++
src/preferences/prefs-appearance-page.h | 32 +
src/preferences/prefs-dialog.c | 73 +
src/{ => preferences}/prefs-dialog.h | 0
src/preferences/prefs-general-page.c | 1378 ++++++++++
src/preferences/prefs-general-page.h | 37 +
src/preferences/prefs-privacy-page.c | 243 ++
src/preferences/prefs-privacy-page.h | 32 +
src/preferences/prefs-sync-page.c | 790 ++++++
src/preferences/prefs-sync-page.h | 34 +
src/{ => preferences}/synced-tabs-dialog.c | 0
src/{ => preferences}/synced-tabs-dialog.h | 0
.../webapp-additional-urls-dialog.c | 0
.../webapp-additional-urls-dialog.h | 0
src/prefs-dialog.c | 2640 --------------------
src/resources/epiphany.gresource.xml | 4 +
src/resources/gtk/prefs-appearance-page.ui | 216 ++
src/resources/gtk/prefs-dialog.ui | 951 +------
src/resources/gtk/prefs-general-page.ui | 337 +++
src/resources/gtk/prefs-privacy-page.ui | 209 ++
src/resources/gtk/prefs-sync-page.ui | 205 ++
30 files changed, 4032 insertions(+), 3594 deletions(-)
---
diff --git a/src/meson.build b/src/meson.build
index 2a97287dd..718b278d6 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -19,8 +19,6 @@ libephymain_sources = [
'bookmarks/ephy-bookmarks-import.c',
'bookmarks/ephy-bookmarks-manager.c',
'bookmarks/ephy-bookmarks-popover.c',
- 'clear-data-dialog.c',
- 'cookies-dialog.c',
'ephy-action-bar.c',
'ephy-action-bar-end.c',
'ephy-action-bar-start.c',
@@ -40,18 +38,24 @@ libephymain_sources = [
'ephy-pages-button.c',
'ephy-pages-popover.c',
'ephy-pages-view.c',
- 'ephy-search-engine-dialog.c',
'ephy-session.c',
'ephy-shell.c',
'ephy-suggestion-model.c',
'ephy-tab-header-bar.c',
'ephy-tab-label.c',
'ephy-window.c',
- 'passwords-dialog.c',
'popup-commands.c',
- 'prefs-dialog.c',
- 'synced-tabs-dialog.c',
- 'webapp-additional-urls-dialog.c',
+ 'preferences/clear-data-dialog.c',
+ 'preferences/cookies-dialog.c',
+ 'preferences/ephy-search-engine-dialog.c',
+ 'preferences/passwords-dialog.c',
+ 'preferences/prefs-appearance-page.c',
+ 'preferences/prefs-dialog.c',
+ 'preferences/prefs-general-page.c',
+ 'preferences/prefs-privacy-page.c',
+ 'preferences/prefs-sync-page.c',
+ 'preferences/synced-tabs-dialog.c',
+ 'preferences/webapp-additional-urls-dialog.c',
'window-commands.c',
compile_schemas,
enums
@@ -71,6 +75,7 @@ libephymain_deps = [
libephymain_includes = include_directories(
'.',
'bookmarks',
+ 'preferences',
)
libephymain = shared_library('ephymain',
diff --git a/src/clear-data-dialog.c b/src/preferences/clear-data-dialog.c
similarity index 100%
rename from src/clear-data-dialog.c
rename to src/preferences/clear-data-dialog.c
diff --git a/src/clear-data-dialog.h b/src/preferences/clear-data-dialog.h
similarity index 100%
rename from src/clear-data-dialog.h
rename to src/preferences/clear-data-dialog.h
diff --git a/src/cookies-dialog.c b/src/preferences/cookies-dialog.c
similarity index 100%
rename from src/cookies-dialog.c
rename to src/preferences/cookies-dialog.c
diff --git a/src/cookies-dialog.h b/src/preferences/cookies-dialog.h
similarity index 100%
rename from src/cookies-dialog.h
rename to src/preferences/cookies-dialog.h
diff --git a/src/ephy-search-engine-dialog.c b/src/preferences/ephy-search-engine-dialog.c
similarity index 100%
rename from src/ephy-search-engine-dialog.c
rename to src/preferences/ephy-search-engine-dialog.c
diff --git a/src/ephy-search-engine-dialog.h b/src/preferences/ephy-search-engine-dialog.h
similarity index 100%
rename from src/ephy-search-engine-dialog.h
rename to src/preferences/ephy-search-engine-dialog.h
diff --git a/src/passwords-dialog.c b/src/preferences/passwords-dialog.c
similarity index 100%
rename from src/passwords-dialog.c
rename to src/preferences/passwords-dialog.c
diff --git a/src/passwords-dialog.h b/src/preferences/passwords-dialog.h
similarity index 100%
rename from src/passwords-dialog.h
rename to src/preferences/passwords-dialog.h
diff --git a/src/preferences/prefs-appearance-page.c b/src/preferences/prefs-appearance-page.c
new file mode 100644
index 000000000..dc14a6344
--- /dev/null
+++ b/src/preferences/prefs-appearance-page.c
@@ -0,0 +1,426 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of Epiphany.
+ *
+ * Epiphany is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "prefs-appearance-page.h"
+
+#include "ephy-embed-prefs.h"
+#include "ephy-file-helpers.h"
+#include "ephy-flatpak-utils.h"
+#include "ephy-lib-type-builtins.h"
+#include "ephy-settings.h"
+#include "ephy-shell.h"
+
+#include <math.h>
+
+struct _PrefsAppearancePage {
+ HdyPreferencesPage parent_instance;
+
+ /* Fonts */
+ GtkWidget *use_gnome_fonts_row;
+ GtkWidget *use_custom_fonts_list;
+ GtkWidget *sans_fontbutton;
+ GtkWidget *serif_fontbutton;
+ GtkWidget *mono_fontbutton;
+
+ /* Reader Mode */
+ GtkWidget *reader_mode_box;
+ GtkWidget *reader_mode_font_style;
+ GtkWidget *reader_mode_color_scheme;
+
+ /* Style */
+ GtkWidget *css_switch;
+ GtkWidget *css_edit_button;
+ GtkWidget *js_switch;
+ GtkWidget *js_edit_button;
+ GtkWidget *default_zoom_spin_button;
+};
+
+G_DEFINE_TYPE (PrefsAppearancePage, prefs_appearance_page, HDY_TYPE_PREFERENCES_PAGE)
+
+static gchar *
+reader_font_style_get_name (HdyEnumValueObject *value,
+ gpointer user_data)
+{
+ g_assert (HDY_IS_ENUM_VALUE_OBJECT (value));
+
+ switch (hdy_enum_value_object_get_value (value)) {
+ case EPHY_PREFS_READER_FONT_STYLE_SANS:
+ return g_strdup (_("Sans"));
+ case EPHY_PREFS_READER_FONT_STYLE_SERIF:
+ return g_strdup (_("Serif"));
+ default:
+ return NULL;
+ }
+}
+
+static gboolean
+reader_font_style_get_mapping (GValue *value,
+ GVariant *variant,
+ gpointer user_data)
+{
+ const char *reader_colors = g_variant_get_string (variant, NULL);
+
+ if (g_strcmp0 (reader_colors, "sans") == 0)
+ g_value_set_int (value, EPHY_PREFS_READER_FONT_STYLE_SANS);
+ else if (g_strcmp0 (reader_colors, "serif") == 0)
+ g_value_set_int (value, EPHY_PREFS_READER_FONT_STYLE_SERIF);
+
+ return TRUE;
+}
+
+static GVariant *
+reader_font_style_set_mapping (const GValue *value,
+ const GVariantType *expected_type,
+ gpointer user_data)
+{
+ switch (g_value_get_int (value)) {
+ case EPHY_PREFS_READER_FONT_STYLE_SANS:
+ return g_variant_new_string ("sans");
+ case EPHY_PREFS_READER_FONT_STYLE_SERIF:
+ return g_variant_new_string ("serif");
+ default:
+ return g_variant_new_string ("crashed");
+ }
+}
+
+static gchar *
+reader_color_scheme_get_name (HdyEnumValueObject *value,
+ gpointer user_data)
+{
+ g_assert (HDY_IS_ENUM_VALUE_OBJECT (value));
+
+ switch (hdy_enum_value_object_get_value (value)) {
+ case EPHY_PREFS_READER_COLORS_LIGHT:
+ return g_strdup (_("Light"));
+ case EPHY_PREFS_READER_COLORS_DARK:
+ return g_strdup (_("Dark"));
+ default:
+ return NULL;
+ }
+}
+
+static gboolean
+reader_color_scheme_get_mapping (GValue *value,
+ GVariant *variant,
+ gpointer user_data)
+{
+ const char *reader_colors = g_variant_get_string (variant, NULL);
+
+ if (g_strcmp0 (reader_colors, "light") == 0)
+ g_value_set_int (value, EPHY_PREFS_READER_COLORS_LIGHT);
+ else if (g_strcmp0 (reader_colors, "dark") == 0)
+ g_value_set_int (value, EPHY_PREFS_READER_COLORS_DARK);
+
+ return TRUE;
+}
+
+static GVariant *
+reader_color_scheme_set_mapping (const GValue *value,
+ const GVariantType *expected_type,
+ gpointer user_data)
+{
+ switch (g_value_get_int (value)) {
+ case EPHY_PREFS_READER_COLORS_LIGHT:
+ return g_variant_new_string ("light");
+ case EPHY_PREFS_READER_COLORS_DARK:
+ return g_variant_new_string ("dark");
+ default:
+ return g_variant_new_string ("crashed");
+ }
+}
+
+static void
+css_file_opened_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ gboolean ret;
+ GError *error = NULL;
+
+ ret = ephy_open_file_via_flatpak_portal_finish (result, &error);
+ if (!ret) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Failed to open CSS file: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+css_file_created_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = G_FILE (source);
+ GFileOutputStream *stream;
+ GError *error = NULL;
+
+ stream = g_file_create_finish (file, result, &error);
+ if (stream == NULL && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ g_warning ("Failed to create %s: %s", g_file_get_path (file), error->message);
+ else {
+ if (ephy_is_running_inside_flatpak ())
+ ephy_open_file_via_flatpak_portal (g_file_get_path (file), NULL, css_file_opened_cb, NULL);
+ else
+ ephy_file_launch_handler (file, gtk_get_current_event_time ());
+ }
+
+ if (error != NULL)
+ g_error_free (error);
+ if (stream != NULL)
+ g_object_unref (stream);
+ g_object_unref (file);
+}
+
+static void
+css_edit_button_clicked_cb (GtkWidget *button,
+ PrefsAppearancePage *appearance_page)
+{
+ GFile *css_file;
+
+ css_file = g_file_new_for_path (g_build_filename (ephy_profile_dir (),
+ USER_STYLESHEET_FILENAME,
+ NULL));
+
+ g_file_create_async (css_file, G_FILE_CREATE_NONE, G_PRIORITY_DEFAULT, NULL, css_file_created_cb, NULL);
+}
+
+static void
+js_file_opened_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ gboolean ret;
+ g_autoptr (GError) error = NULL;
+
+ ret = ephy_open_file_via_flatpak_portal_finish (result, &error);
+ if (!ret && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Failed to open JS file: %s", error->message);
+}
+
+static void
+js_file_created_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GFile) file = G_FILE (source);
+ g_autoptr (GFileOutputStream) stream = NULL;
+ g_autoptr (GError) error = NULL;
+
+ stream = g_file_create_finish (file, result, &error);
+ if (stream == NULL && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ g_warning ("Failed to create %s: %s", g_file_get_path (file), error->message);
+ else {
+ if (ephy_is_running_inside_flatpak ())
+ ephy_open_file_via_flatpak_portal (g_file_get_path (file), NULL, js_file_opened_cb, NULL);
+ else
+ ephy_file_launch_handler (file, gtk_get_current_event_time ());
+ }
+}
+
+static void
+js_edit_button_clicked_cb (GtkWidget *button,
+ PrefsAppearancePage *appearance_page)
+{
+ GFile *js_file;
+
+ js_file = g_file_new_for_path (g_build_filename (ephy_profile_dir (),
+ USER_JAVASCRIPT_FILENAME,
+ NULL));
+
+ g_file_create_async (g_steal_pointer (&js_file), G_FILE_CREATE_NONE, G_PRIORITY_DEFAULT, NULL,
js_file_created_cb, NULL);
+}
+
+static gboolean
+on_default_zoom_spin_button_output (GtkSpinButton *spin,
+ gpointer user_data)
+{
+ GtkAdjustment *adjustment;
+ g_autofree gchar *text = NULL;
+ gdouble value;
+
+ adjustment = gtk_spin_button_get_adjustment (spin);
+ value = (int)gtk_adjustment_get_value (adjustment);
+ text = g_strdup_printf ("%.f%%", value);
+ gtk_entry_set_text (GTK_ENTRY (spin), text);
+
+ return TRUE;
+}
+
+static void
+on_default_zoom_spin_button_value_changed (GtkSpinButton *spin,
+ gpointer user_data)
+{
+ GtkAdjustment *adjustment;
+ gdouble value;
+
+ adjustment = gtk_spin_button_get_adjustment (spin);
+ value = gtk_adjustment_get_value (adjustment);
+ value = roundf (value) / 100;
+ g_settings_set_double (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_DEFAULT_ZOOM_LEVEL, value);
+}
+
+static void
+setup_appearance_page (PrefsAppearancePage *appearance_page)
+{
+ GSettings *web_settings = ephy_settings_get (EPHY_PREFS_WEB_SCHEMA);
+ GSettings *reader_settings = ephy_settings_get (EPHY_PREFS_READER_SCHEMA);
+
+ /* ======================================================================== */
+ /* ========================== Fonts ======================================= */
+ /* ======================================================================== */
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_USE_GNOME_FONTS,
+ appearance_page->use_gnome_fonts_row,
+ "enable-expansion",
+ G_SETTINGS_BIND_INVERT_BOOLEAN);
+
+ gtk_list_box_set_header_func (GTK_LIST_BOX (appearance_page->use_custom_fonts_list),
+ hdy_list_box_separator_header, NULL, NULL);
+
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_SANS_SERIF_FONT,
+ appearance_page->sans_fontbutton,
+ "font-name",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_SERIF_FONT,
+ appearance_page->serif_fontbutton,
+ "font-name",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_MONOSPACE_FONT,
+ appearance_page->mono_fontbutton,
+ "font-name",
+ G_SETTINGS_BIND_DEFAULT);
+
+ /* ======================================================================== */
+ /* ========================== Reader Mode ================================= */
+ /* ======================================================================== */
+ hdy_combo_row_set_for_enum (HDY_COMBO_ROW (appearance_page->reader_mode_font_style),
+ EPHY_TYPE_PREFS_READER_FONT_STYLE,
+ reader_font_style_get_name, NULL, NULL);
+
+ g_settings_bind_with_mapping (reader_settings,
+ EPHY_PREFS_READER_FONT_STYLE,
+ appearance_page->reader_mode_font_style,
+ "selected-index",
+ G_SETTINGS_BIND_DEFAULT,
+ reader_font_style_get_mapping,
+ reader_font_style_set_mapping,
+ NULL, NULL);
+
+ hdy_combo_row_set_for_enum (HDY_COMBO_ROW (appearance_page->reader_mode_color_scheme),
+ EPHY_TYPE_PREFS_READER_COLOR_SCHEME,
+ reader_color_scheme_get_name, NULL, NULL);
+
+ g_settings_bind_with_mapping (reader_settings,
+ EPHY_PREFS_READER_COLOR_SCHEME,
+ appearance_page->reader_mode_color_scheme,
+ "selected-index",
+ G_SETTINGS_BIND_DEFAULT,
+ reader_color_scheme_get_mapping,
+ reader_color_scheme_set_mapping,
+ NULL, NULL);
+
+ /* ======================================================================== */
+ /* ========================== Style ======================================= */
+ /* ======================================================================== */
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_ENABLE_USER_CSS,
+ appearance_page->css_switch,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_ENABLE_USER_CSS,
+ appearance_page->css_edit_button,
+ "sensitive",
+ G_SETTINGS_BIND_GET);
+
+ g_signal_connect (appearance_page->css_edit_button,
+ "clicked",
+ G_CALLBACK (css_edit_button_clicked_cb),
+ appearance_page);
+
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_ENABLE_USER_JS,
+ appearance_page->js_switch,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_ENABLE_USER_JS,
+ appearance_page->js_edit_button,
+ "sensitive",
+ G_SETTINGS_BIND_GET);
+
+ g_signal_connect (appearance_page->js_edit_button,
+ "clicked",
+ G_CALLBACK (js_edit_button_clicked_cb),
+ appearance_page);
+
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (appearance_page->default_zoom_spin_button),
+ g_settings_get_double (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_DEFAULT_ZOOM_LEVEL) *
100);
+}
+
+static void
+prefs_appearance_page_class_init (PrefsAppearancePageClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gnome/epiphany/gtk/prefs-appearance-page.ui");
+
+ /* Fonts */
+ gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, use_gnome_fonts_row);
+ gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, use_custom_fonts_list);
+ gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, sans_fontbutton);
+ gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, serif_fontbutton);
+ gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, mono_fontbutton);
+
+ /* Reader Mode */
+ gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, reader_mode_box);
+ gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, reader_mode_font_style);
+ gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, reader_mode_color_scheme);
+
+ /* Style */
+ gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, css_switch);
+ gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, css_edit_button);
+ gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, js_switch);
+ gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, js_edit_button);
+ gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, default_zoom_spin_button);
+
+ /* Signals */
+ gtk_widget_class_bind_template_callback (widget_class, on_default_zoom_spin_button_output);
+ gtk_widget_class_bind_template_callback (widget_class, on_default_zoom_spin_button_value_changed);
+}
+
+static void
+prefs_appearance_page_init (PrefsAppearancePage *appearance_page)
+{
+ EphyEmbedShellMode mode = ephy_embed_shell_get_mode (ephy_embed_shell_get_default ());
+
+ gtk_widget_init_template (GTK_WIDGET (appearance_page));
+
+ gtk_widget_set_visible (appearance_page->reader_mode_box,
+ mode != EPHY_EMBED_SHELL_MODE_APPLICATION);
+
+ setup_appearance_page (appearance_page);
+}
diff --git a/src/preferences/prefs-appearance-page.h b/src/preferences/prefs-appearance-page.h
new file mode 100644
index 000000000..ba511b4db
--- /dev/null
+++ b/src/preferences/prefs-appearance-page.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of Epiphany.
+ *
+ * Epiphany is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+#define HANDY_USE_UNSTABLE_API
+#include <handy.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_PREFS_APPEARANCE_PAGE (prefs_appearance_page_get_type ())
+
+G_DECLARE_FINAL_TYPE (PrefsAppearancePage, prefs_appearance_page, EPHY, PREFS_APPEARANCE_PAGE,
HdyPreferencesPage)
+
+G_END_DECLS
diff --git a/src/preferences/prefs-dialog.c b/src/preferences/prefs-dialog.c
new file mode 100644
index 000000000..7755674c0
--- /dev/null
+++ b/src/preferences/prefs-dialog.c
@@ -0,0 +1,73 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 200-2003 Marco Pesenti Gritti
+ * Copyright © 2003, 2004, 2005 Christian Persch
+ * Copyright © 2010, 2017 Igalia S.L.
+ *
+ * This file is part of Epiphany.
+ *
+ * Epiphany is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "prefs-dialog.h"
+
+#include "ephy-embed-utils.h"
+#include "ephy-gui.h"
+#include "prefs-general-page.h"
+#include "prefs-sync-page.h"
+
+#include <gtk/gtk.h>
+
+struct _PrefsDialog {
+ GtkDialog parent_instance;
+
+ GtkWidget *notebook;
+
+ PrefsGeneralPage *general_page;
+ PrefsSyncPage *sync_page;
+};
+
+G_DEFINE_TYPE (PrefsDialog, prefs_dialog, GTK_TYPE_DIALOG)
+
+static void
+prefs_dialog_class_init (PrefsDialogClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gnome/epiphany/gtk/prefs-dialog.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, PrefsDialog, notebook);
+ gtk_widget_class_bind_template_child (widget_class, PrefsDialog, general_page);
+ gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_page);
+}
+
+static void
+prefs_dialog_init (PrefsDialog *dialog)
+{
+ EphyEmbedShellMode mode = ephy_embed_shell_get_mode (ephy_embed_shell_get_default ());
+
+ gtk_widget_init_template (GTK_WIDGET (dialog));
+ gtk_window_set_icon_name (GTK_WINDOW (dialog), APPLICATION_ID);
+
+ prefs_general_page_connect_pd_response (dialog->general_page, dialog);
+
+ if (mode == EPHY_EMBED_SHELL_MODE_BROWSER)
+ prefs_sync_page_setup (dialog->sync_page);
+ else
+ gtk_notebook_remove_page (GTK_NOTEBOOK (dialog->notebook), -1);
+
+ ephy_gui_ensure_window_group (GTK_WINDOW (dialog));
+}
diff --git a/src/prefs-dialog.h b/src/preferences/prefs-dialog.h
similarity index 100%
rename from src/prefs-dialog.h
rename to src/preferences/prefs-dialog.h
diff --git a/src/preferences/prefs-general-page.c b/src/preferences/prefs-general-page.c
new file mode 100644
index 000000000..682496be3
--- /dev/null
+++ b/src/preferences/prefs-general-page.c
@@ -0,0 +1,1378 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of Epiphany.
+ *
+ * Epiphany is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "prefs-general-page.h"
+
+#include "ephy-embed-shell.h"
+#include "ephy-file-chooser.h"
+#include "ephy-file-helpers.h"
+#include "ephy-flatpak-utils.h"
+#include "ephy-langs.h"
+#include "ephy-settings.h"
+#include "ephy-search-engine-dialog.h"
+#include "ephy-web-app-utils.h"
+#include "webapp-additional-urls-dialog.h"
+
+#include "gnome-languages.h"
+#include <glib/gi18n.h>
+
+#define DOWNLOAD_BUTTON_WIDTH 8
+
+enum {
+ COL_LANG_NAME,
+ COL_LANG_CODE
+};
+
+struct _PrefsGeneralPage {
+ HdyPreferencesPage parent_instance;
+
+ /* Web Application */
+ EphyWebApplication *webapp;
+ guint webapp_save_id;
+ GtkWidget *webapp_box;
+ GtkWidget *webapp_icon;
+ GtkWidget *webapp_url;
+ GtkWidget *webapp_title;
+
+ /* Homepage */
+ GtkWidget *homepage_box;
+ GtkWidget *new_tab_homepage_radiobutton;
+ GtkWidget *blank_homepage_radiobutton;
+ GtkWidget *custom_homepage_radiobutton;
+ GtkWidget *custom_homepage_entry;
+
+ /* Downloads */
+ GtkWidget *download_box;
+ GtkWidget *ask_on_download_switch;
+ GtkWidget *download_folder_row;
+
+ /* Search Engines */
+ GtkWidget *search_box;
+
+ /* Session */
+ GtkWidget *session_box;
+ GtkWidget *start_in_incognito_mode_switch;
+ GtkWidget *restore_session_row;
+ GtkWidget *restore_session_switch;
+
+ /* Browsing */
+ GtkWidget *browsing_box;
+ GtkWidget *enable_smooth_scrolling_switch;
+ GtkWidget *enable_mouse_gesture_switch;
+
+ /* Languages */
+ HdyPreferencesGroup *lang_group;
+ GtkWidget *lang_listbox;
+ GtkWidget *enable_spell_checking_switch;
+
+ GtkDialog *add_lang_dialog;
+ GtkTreeView *add_lang_treeview;
+};
+
+G_DEFINE_TYPE (PrefsGeneralPage, prefs_general_page, HDY_TYPE_PREFERENCES_PAGE)
+
+static void
+prefs_general_page_finalize (GObject *object)
+{
+ PrefsGeneralPage *general_page = EPHY_PREFS_GENERAL_PAGE (object);
+
+ if (general_page->add_lang_dialog != NULL) {
+ GtkDialog **add_lang_dialog = &general_page->add_lang_dialog;
+
+ g_object_remove_weak_pointer (G_OBJECT (general_page->add_lang_dialog),
+ (gpointer *)add_lang_dialog);
+ g_object_unref (general_page->add_lang_dialog);
+ }
+
+ g_clear_pointer (&general_page->webapp, ephy_web_application_free);
+ G_OBJECT_CLASS (prefs_general_page_parent_class)->finalize (object);
+}
+
+static int
+get_list_box_length (GtkWidget *list_box)
+{
+ GList *children = gtk_container_get_children (GTK_CONTAINER (list_box));
+
+ return g_list_length (children);
+}
+
+static void
+language_editor_update_pref (PrefsGeneralPage *general_page)
+{
+ GVariantBuilder builder;
+ GtkListBoxRow *row;
+ int index = 0;
+
+ if (get_list_box_length (general_page->lang_listbox) <= 1) {
+ g_settings_set (EPHY_SETTINGS_WEB,
+ EPHY_PREFS_WEB_LANGUAGE,
+ "as", NULL);
+ return;
+ }
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_STRING_ARRAY);
+
+ while ((row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (general_page->lang_listbox), index++))) {
+ char *code;
+
+ code = g_object_get_data (G_OBJECT (row), "code");
+ if (code)
+ g_variant_builder_add (&builder, "s", code);
+ }
+
+ g_settings_set (EPHY_SETTINGS_WEB,
+ EPHY_PREFS_WEB_LANGUAGE,
+ "as", &builder);
+}
+
+static void
+drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint32 time,
+ gpointer data)
+{
+ GtkWidget *row_before;
+ GtkWidget *row_after;
+ GtkWidget *row;
+ GtkWidget *source;
+ PrefsGeneralPage *general_page = data;
+ int len;
+ int pos;
+
+ row_before = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-before"));
+ row_after = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-after"));
+
+ g_object_set_data (G_OBJECT (widget), "row-before", NULL);
+ g_object_set_data (G_OBJECT (widget), "row-after", NULL);
+
+ if (row_before)
+ gtk_style_context_remove_class (gtk_widget_get_style_context (row_before), "drag-hover-bottom");
+ if (row_after)
+ gtk_style_context_remove_class (gtk_widget_get_style_context (row_after), "drag-hover-top");
+
+ row = (gpointer) * (gpointer *)gtk_selection_data_get_data (selection_data);
+ source = gtk_widget_get_ancestor (row, GTK_TYPE_LIST_BOX_ROW);
+
+ if (source == row_after)
+ return;
+
+ len = get_list_box_length (general_page->lang_listbox);
+ g_object_ref (source);
+ gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (source)), source);
+
+ if (row_after)
+ pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (row_after));
+ else
+ pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (row_before)) + 1;
+
+ if (pos + 1 == len)
+ pos--;
+
+ gtk_list_box_insert (GTK_LIST_BOX (widget), source, pos);
+ g_object_unref (source);
+
+ language_editor_update_pref (general_page);
+}
+
+static GtkListBoxRow *
+get_row_before (GtkListBox *list,
+ GtkListBoxRow *row)
+{
+ int pos = gtk_list_box_row_get_index (row);
+ return gtk_list_box_get_row_at_index (list, pos - 1);
+}
+
+static GtkListBoxRow *
+get_row_after (GtkListBox *list,
+ GtkListBoxRow *row)
+{
+ int pos = gtk_list_box_row_get_index (row);
+ return gtk_list_box_get_row_at_index (list, pos + 1);
+}
+
+static GtkListBoxRow *
+get_last_row (GtkListBox *list)
+{
+ int i;
+
+ for (i = 0; ; i++) {
+ GtkListBoxRow *tmp;
+ tmp = gtk_list_box_get_row_at_index (list, i);
+ if (tmp == NULL)
+ break;
+ }
+
+ return i > 0 ? gtk_list_box_get_row_at_index (list, i - 1) : NULL;
+}
+
+static gboolean
+drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ int x,
+ int y,
+ guint time)
+{
+ GtkAllocation alloc;
+ GtkWidget *row;
+ int hover_row_y;
+ int hover_row_height;
+ GtkWidget *drag_row;
+ GtkWidget *row_before;
+ GtkWidget *row_after;
+
+ row = GTK_WIDGET (gtk_list_box_get_row_at_y (GTK_LIST_BOX (widget), y));
+
+ drag_row = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "drag-row"));
+ row_before = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-before"));
+ row_after = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-after"));
+
+ gtk_style_context_remove_class (gtk_widget_get_style_context (drag_row), "drag-hover");
+ if (row_before)
+ gtk_style_context_remove_class (gtk_widget_get_style_context (row_before), "drag-hover-bottom");
+ if (row_after)
+ gtk_style_context_remove_class (gtk_widget_get_style_context (row_after), "drag-hover-top");
+
+ if (row) {
+ gtk_widget_get_allocation (row, &alloc);
+ hover_row_y = alloc.y;
+ hover_row_height = alloc.height;
+
+ if (y < hover_row_y + hover_row_height / 2) {
+ row_after = row;
+ row_before = GTK_WIDGET (get_row_before (GTK_LIST_BOX (widget), GTK_LIST_BOX_ROW (row)));
+ } else {
+ row_before = row;
+ row_after = GTK_WIDGET (get_row_after (GTK_LIST_BOX (widget), GTK_LIST_BOX_ROW (row)));
+ }
+ } else {
+ row_before = GTK_WIDGET (get_last_row (GTK_LIST_BOX (widget)));
+ row_after = NULL;
+ }
+
+ g_object_set_data (G_OBJECT (widget), "row-before", row_before);
+ g_object_set_data (G_OBJECT (widget), "row-after", row_after);
+
+ if (drag_row == row_before || drag_row == row_after) {
+ gtk_style_context_add_class (gtk_widget_get_style_context (drag_row), "drag-hover");
+ return FALSE;
+ }
+
+ if (row_before)
+ gtk_style_context_add_class (gtk_widget_get_style_context (row_before), "drag-hover-bottom");
+ if (row_after)
+ gtk_style_context_add_class (gtk_widget_get_style_context (row_after), "drag-hover-top");
+
+ return TRUE;
+}
+
+static void
+drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time)
+{
+ GtkWidget *drag_row;
+ GtkWidget *row_before;
+ GtkWidget *row_after;
+
+ drag_row = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "drag-row"));
+ row_before = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-before"));
+ row_after = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-after"));
+
+ gtk_style_context_remove_class (gtk_widget_get_style_context (drag_row), "drag-hover");
+ if (row_before)
+ gtk_style_context_remove_class (gtk_widget_get_style_context (row_before), "drag-hover-bottom");
+ if (row_after)
+ gtk_style_context_remove_class (gtk_widget_get_style_context (row_after), "drag-hover-top");
+}
+
+static GtkDialog *setup_add_language_dialog (PrefsGeneralPage *general_page);
+
+static void
+language_editor_add_button_release_event (GtkWidget *button,
+ GdkEvent *event,
+ PrefsGeneralPage *general_page)
+{
+ if (general_page->add_lang_dialog == NULL) {
+ GtkDialog **add_lang_dialog;
+
+ general_page->add_lang_dialog = setup_add_language_dialog (general_page);
+ gtk_window_set_transient_for (GTK_WINDOW (general_page->add_lang_dialog), GTK_WINDOW (general_page));
+
+ add_lang_dialog = &general_page->add_lang_dialog;
+
+ g_object_add_weak_pointer
+ (G_OBJECT (general_page->add_lang_dialog),
+ (gpointer *)add_lang_dialog);
+ }
+
+ gtk_window_present_with_time (GTK_WINDOW (general_page->add_lang_dialog), gtk_get_current_event_time ());
+}
+
+static void
+language_editor_add_function_buttons (PrefsGeneralPage *general_page)
+{
+ GtkWidget *box;
+ GtkWidget *label;
+
+ box = gtk_event_box_new ();
+ label = gtk_label_new (_("Add Language"));
+ g_signal_connect (box, "button-release-event", G_CALLBACK (language_editor_add_button_release_event),
general_page);
+ gtk_container_add (GTK_CONTAINER (box), label);
+ gtk_widget_set_size_request (box, -1, 50);
+ gtk_widget_show_all (GTK_WIDGET (box));
+
+ gtk_list_box_insert (GTK_LIST_BOX (general_page->lang_listbox), box, -1);
+}
+
+static void
+language_editor_update_state (PrefsGeneralPage *general_page)
+{
+ int length = get_list_box_length (general_page->lang_listbox);
+ int index;
+
+ if (length == 2) {
+ GtkListBoxRow *row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (general_page->lang_listbox), 0);
+ GtkWidget *action = g_object_get_data (G_OBJECT (row), "action");
+
+ gtk_widget_set_sensitive (action, FALSE);
+ return;
+ }
+
+ for (index = 0; index < length - 1; index++) {
+ GtkListBoxRow *row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (general_page->lang_listbox), index);
+ GtkWidget *action = g_object_get_data (G_OBJECT (row), "action");
+
+ gtk_widget_set_sensitive (action, TRUE);
+ }
+}
+
+static void
+language_editor_remove_button_clicked_cb (GtkWidget *button,
+ PrefsGeneralPage *general_page)
+{
+ GtkWidget *row = g_object_get_data (G_OBJECT (button), "row");
+
+ if (row) {
+ gtk_container_remove (GTK_CONTAINER (general_page->lang_listbox), row);
+ language_editor_update_pref (general_page);
+ language_editor_update_state (general_page);
+ }
+}
+
+void
+drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time,
+ gpointer data)
+{
+ gtk_selection_data_set (selection_data,
+ gdk_atom_intern_static_string ("GTK_LIST_BOX_ROW"),
+ 32,
+ (const guchar *)&widget,
+ sizeof (gpointer));
+}
+
+static void
+drag_begin (GtkWidget *widget,
+ GdkDragContext *context,
+ gpointer data)
+{
+ GtkWidget *row;
+ GtkAllocation alloc;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ int x, y;
+ double sx, sy;
+
+ row = gtk_widget_get_ancestor (widget, GTK_TYPE_LIST_BOX_ROW);
+ gtk_widget_get_allocation (row, &alloc);
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, alloc.width, alloc.height);
+ cr = cairo_create (surface);
+
+ gtk_style_context_add_class (gtk_widget_get_style_context (row), "drag-icon");
+ gtk_widget_draw (row, cr);
+ gtk_style_context_remove_class (gtk_widget_get_style_context (row), "drag-icon");
+
+ gtk_widget_translate_coordinates (widget, row, 0, 0, &x, &y);
+ cairo_surface_get_device_scale (surface, &sx, &sy);
+ cairo_surface_set_device_offset (surface, -x * sy, -y * sy);
+ gtk_drag_set_icon_surface (context, surface);
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+
+ g_object_set_data (G_OBJECT (gtk_widget_get_parent (row)), "drag-row", row);
+ gtk_style_context_add_class (gtk_widget_get_style_context (row), "drag-row");
+}
+
+static void
+drag_end (GtkWidget *widget,
+ GdkDragContext *context,
+ gpointer data)
+{
+ GtkWidget *row;
+
+ row = gtk_widget_get_ancestor (widget, GTK_TYPE_LIST_BOX_ROW);
+ g_object_set_data (G_OBJECT (gtk_widget_get_parent (row)), "drag-row", NULL);
+ gtk_style_context_remove_class (gtk_widget_get_style_context (row), "drag-row");
+ gtk_style_context_remove_class (gtk_widget_get_style_context (row), "drag-hover");
+}
+
+static GtkTargetEntry entries[] = {
+ { "GTK_LIST_BOX_ROW", GTK_TARGET_SAME_APP, 0 }
+};
+
+static void
+language_editor_add (PrefsGeneralPage *general_page,
+ const char *code,
+ const char *desc)
+{
+ GtkWidget *event_box;
+ HdyActionRow *row;
+ GtkWidget *prefix;
+ GtkWidget *action;
+ int len;
+ int index;
+
+ g_assert (code != NULL && desc != NULL);
+
+ len = get_list_box_length (general_page->lang_listbox);
+
+ for (index = 0; index < len; index++) {
+ GtkListBoxRow *widget;
+ char *row_code;
+
+ widget = gtk_list_box_get_row_at_index (GTK_LIST_BOX (general_page->lang_listbox), index++);
+
+ row_code = g_object_get_data (G_OBJECT (widget), "code");
+ if (row_code && strcmp (row_code, code) == 0)
+ return;
+ }
+
+ row = hdy_action_row_new ();
+ hdy_action_row_set_title (row, desc);
+ g_object_set_data (G_OBJECT (row), "code", g_strdup (code));
+ gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (row)), "row");
+
+ event_box = gtk_event_box_new ();
+ gtk_drag_source_set (GTK_WIDGET (event_box), GDK_BUTTON1_MASK, entries, 1, GDK_ACTION_MOVE);
+ g_signal_connect (event_box, "drag-begin", G_CALLBACK (drag_begin), general_page);
+ g_signal_connect (event_box, "drag-end", G_CALLBACK (drag_end), general_page);
+ g_signal_connect (event_box, "drag-data-get", G_CALLBACK (drag_data_get), general_page);
+ prefix = gtk_image_new_from_icon_name ("list-drag-handle-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_container_add (GTK_CONTAINER (event_box), prefix);
+ hdy_action_row_add_prefix (row, event_box);
+
+ action = gtk_button_new_from_icon_name ("edit-delete-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_widget_set_tooltip_text (action, _("Delete language"));
+ g_object_set_data (G_OBJECT (row), "action", action);
+ g_object_set_data (G_OBJECT (action), "row", row);
+ g_signal_connect (action, "clicked", G_CALLBACK (language_editor_remove_button_clicked_cb), general_page);
+ gtk_widget_set_valign (action, GTK_ALIGN_CENTER);
+ hdy_action_row_add_action (row, action);
+
+ gtk_widget_show_all (GTK_WIDGET (row));
+
+ gtk_list_box_insert (GTK_LIST_BOX (general_page->lang_listbox), GTK_WIDGET (row), len - 1);
+}
+
+static void
+add_lang_dialog_response_cb (GtkWidget *widget,
+ int response,
+ PrefsGeneralPage *general_page)
+{
+ GtkDialog *dialog = general_page->add_lang_dialog;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ GList *rows, *r;
+
+ g_assert (dialog != NULL);
+
+ if (response == GTK_RESPONSE_ACCEPT) {
+ selection = gtk_tree_view_get_selection (general_page->add_lang_treeview);
+
+ rows = gtk_tree_selection_get_selected_rows (selection, &model);
+
+ for (r = rows; r != NULL; r = r->next) {
+ GtkTreePath *path = (GtkTreePath *)r->data;
+
+ if (gtk_tree_model_get_iter (model, &iter, path)) {
+ char *code, *desc;
+
+ gtk_tree_model_get (model, &iter,
+ COL_LANG_NAME, &desc,
+ COL_LANG_CODE, &code,
+ -1);
+
+ language_editor_add (general_page, code, desc);
+
+ g_free (desc);
+ g_free (code);
+ }
+ }
+
+ g_list_foreach (rows, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free (rows);
+
+ language_editor_update_pref (general_page);
+ language_editor_update_state (general_page);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+add_lang_dialog_selection_changed (GtkTreeSelection *selection,
+ GtkWidget *button)
+{
+ int n_selected;
+
+ n_selected = gtk_tree_selection_count_selected_rows (selection);
+ gtk_widget_set_sensitive (button, n_selected > 0);
+}
+
+static void
+add_language_add_system_language_entry (GtkListStore *store)
+{
+ GtkTreeIter iter;
+ char **sys_langs;
+ char *system, *text;
+ int n_sys_langs;
+
+ sys_langs = ephy_langs_get_languages ();
+ n_sys_langs = g_strv_length (sys_langs);
+
+ system = g_strjoinv (", ", sys_langs);
+
+ text = g_strdup_printf
+ (ngettext ("System language (%s)",
+ "System languages (%s)", n_sys_langs), system);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COL_LANG_NAME, text,
+ COL_LANG_CODE, "system",
+ -1);
+
+ g_strfreev (sys_langs);
+ g_free (system);
+ g_free (text);
+}
+
+static GtkDialog *
+setup_add_language_dialog (PrefsGeneralPage *general_page)
+{
+ GtkWidget *ad;
+ GtkWidget *add_button;
+ GtkListStore *store;
+ GtkTreeModel *sortmodel;
+ GtkTreeView *treeview;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ guint i, n;
+ GtkBuilder *builder;
+ g_auto (GStrv) locales;
+
+ builder = gtk_builder_new_from_resource ("/org/gnome/epiphany/gtk/prefs-lang-dialog.ui");
+ ad = GTK_WIDGET (gtk_builder_get_object (builder, "add_language_dialog"));
+ add_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_button"));
+ treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "languages_treeview"));
+ general_page->add_lang_treeview = treeview;
+
+ store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+
+ locales = gnome_get_all_locales ();
+ n = g_strv_length (locales);
+
+ for (i = 0; i < n; i++) {
+ const char *locale = locales[i];
+ g_autofree char *language_code = NULL;
+ g_autofree char *country_code = NULL;
+ g_autofree char *language_name = NULL;
+ g_autofree char *shortened_locale = NULL;
+
+ if (!gnome_parse_locale (locale, &language_code, &country_code, NULL, NULL))
+ break;
+
+ if (language_code == NULL)
+ break;
+
+ language_name = gnome_get_language_from_locale (locale, locale);
+
+ if (country_code != NULL)
+ shortened_locale = g_strdup_printf ("%s-%s", language_code, country_code);
+ else
+ shortened_locale = g_strdup (language_code);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COL_LANG_NAME, language_name,
+ COL_LANG_CODE, shortened_locale,
+ -1);
+ }
+
+ add_language_add_system_language_entry (store);
+
+ sortmodel = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
+ gtk_tree_sortable_set_sort_column_id
+ (GTK_TREE_SORTABLE (sortmodel), COL_LANG_NAME, GTK_SORT_ASCENDING);
+
+ gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (general_page)),
+ GTK_WINDOW (ad));
+ gtk_window_set_modal (GTK_WINDOW (ad), TRUE);
+
+ gtk_tree_view_set_reorderable (GTK_TREE_VIEW (treeview), FALSE);
+
+ gtk_tree_view_set_model (treeview, sortmodel);
+
+ gtk_tree_view_set_headers_visible (treeview, FALSE);
+
+ renderer = gtk_cell_renderer_text_new ();
+
+ gtk_tree_view_insert_column_with_attributes (treeview,
+ 0, "Language",
+ renderer,
+ "text", 0,
+ NULL);
+ column = gtk_tree_view_get_column (treeview, 0);
+ gtk_tree_view_column_set_resizable (column, TRUE);
+ gtk_tree_view_column_set_sort_column_id (column, COL_LANG_NAME);
+
+ selection = gtk_tree_view_get_selection (treeview);
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+
+ add_lang_dialog_selection_changed (GTK_TREE_SELECTION (selection), add_button);
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (add_lang_dialog_selection_changed), add_button);
+
+ g_signal_connect (ad, "response",
+ G_CALLBACK (add_lang_dialog_response_cb), general_page);
+
+ g_object_unref (store);
+ g_object_unref (sortmodel);
+
+ return GTK_DIALOG (ad);
+}
+
+static char *
+language_for_locale (const char *locale)
+{
+ g_autoptr (GString) string = g_string_new (locale);
+
+ /* Before calling gnome_get_language_from_locale() we have to convert
+ * from web locales (e.g. es-ES) to UNIX (e.g. es_ES.UTF-8).
+ */
+ g_strdelimit (string->str, "-", '_');
+ g_string_append (string, ".UTF-8");
+
+ return gnome_get_language_from_locale (string->str, string->str);
+}
+
+static char *
+normalize_locale (const char *locale)
+{
+ char *result = g_strdup (locale);
+
+ /* The result we store in prefs looks like es-ES or en-US. We don't
+ * store codeset (not used in Accept-Langs) and we store with hyphen
+ * instead of underscore (ditto). So here we just uppercase the
+ * country code, converting e.g. es-es to es-ES. We have to do this
+ * because older versions of Epiphany stored locales as entirely
+ * lowercase.
+ */
+ for (char *p = strchr (result, '-'); p != NULL && *p != '\0'; p++)
+ *p = g_ascii_toupper (*p);
+
+ return result;
+}
+
+static void
+add_system_language_entry (PrefsGeneralPage *general_page)
+{
+ g_auto (GStrv) sys_langs = NULL;
+ g_autofree char *system = NULL;
+ g_autofree char *text = NULL;
+ int n_sys_langs;
+
+ sys_langs = ephy_langs_get_languages ();
+ n_sys_langs = g_strv_length (sys_langs);
+
+ system = g_strjoinv (", ", sys_langs);
+
+ text = g_strdup_printf
+ (ngettext ("System language (%s)",
+ "System languages (%s)", n_sys_langs), system);
+
+ language_editor_add (general_page, "system", text);
+}
+
+static void
+download_path_changed_cb (GtkFileChooser *button)
+{
+ char *dir;
+
+ dir = gtk_file_chooser_get_filename (button);
+ if (dir == NULL)
+ return;
+
+ g_settings_set_string (EPHY_SETTINGS_STATE,
+ EPHY_PREFS_STATE_DOWNLOAD_DIR, dir);
+ g_free (dir);
+}
+
+static void
+create_download_path_button (PrefsGeneralPage *general_page)
+{
+ GtkWidget *button;
+ char *dir;
+
+ dir = ephy_file_get_downloads_dir ();
+
+ button = gtk_file_chooser_button_new (_("Select a directory"),
+ GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
+
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (button), dir);
+ gtk_file_chooser_button_set_width_chars (GTK_FILE_CHOOSER_BUTTON (button),
+ DOWNLOAD_BUTTON_WIDTH);
+ g_signal_connect (button, "selection-changed",
+ G_CALLBACK (download_path_changed_cb), general_page);
+ hdy_action_row_add_action (HDY_ACTION_ROW (general_page->download_folder_row), button);
+ gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
+ gtk_widget_show (button);
+
+ g_settings_bind_writable (EPHY_SETTINGS_STATE,
+ EPHY_PREFS_STATE_DOWNLOAD_DIR,
+ button, "sensitive", FALSE);
+ g_free (dir);
+}
+
+static gboolean
+restore_session_get_mapping (GValue *value,
+ GVariant *variant,
+ gpointer user_data)
+{
+ const char *policy = g_variant_get_string (variant, NULL);
+ /* FIXME: Is it possible to somehow use EPHY_PREFS_RESTORE_SESSION_POLICY_ALWAYS here? */
+ g_value_set_boolean (value, !strcmp (policy, "always"));
+ return TRUE;
+}
+
+static GVariant *
+restore_session_set_mapping (const GValue *value,
+ const GVariantType *expected_type,
+ gpointer user_data)
+{
+ /* FIXME: Is it possible to somehow use EphyPrefsRestoreSessionPolicy here? */
+ if (g_value_get_boolean (value))
+ return g_variant_new_string ("always");
+ return g_variant_new_string ("crashed");
+}
+
+static gboolean
+save_web_application (PrefsGeneralPage *general_page)
+{
+ gboolean changed = FALSE;
+ const char *text;
+
+ if (!general_page->webapp)
+ return G_SOURCE_REMOVE;
+
+ text = gtk_entry_get_text (GTK_ENTRY (general_page->webapp_url));
+ if (g_strcmp0 (general_page->webapp->url, text) != 0) {
+ g_free (general_page->webapp->url);
+ general_page->webapp->url = g_strdup (text);
+ changed = TRUE;
+ }
+
+ text = gtk_entry_get_text (GTK_ENTRY (general_page->webapp_title));
+ if (g_strcmp0 (general_page->webapp->name, text) != 0) {
+ g_free (general_page->webapp->name);
+ general_page->webapp->name = g_strdup (text);
+ changed = TRUE;
+ }
+
+ text = (const char *)g_object_get_data (G_OBJECT (general_page->webapp_icon), "ephy-webapp-icon-url");
+ if (g_strcmp0 (general_page->webapp->icon_url, text) != 0) {
+ g_free (general_page->webapp->icon_url);
+ general_page->webapp->icon_url = g_strdup (text);
+ changed = TRUE;
+ }
+
+ if (changed)
+ ephy_web_application_save (general_page->webapp);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+prefs_general_page_save_web_application (PrefsGeneralPage *general_page)
+{
+ if (!general_page->webapp)
+ return;
+
+ g_clear_handle_id (&general_page->webapp_save_id, g_source_remove);
+ general_page->webapp_save_id = g_timeout_add_seconds (1, (GSourceFunc)save_web_application, general_page);
+}
+
+static void
+prefs_general_page_update_webapp_icon (PrefsGeneralPage *general_page,
+ const char *icon_url)
+{
+ GdkPixbuf *icon, *scaled_icon;
+ int icon_width, icon_height;
+ double scale;
+
+ icon = gdk_pixbuf_new_from_file (icon_url, NULL);
+ if (!icon)
+ return;
+
+ icon_width = gdk_pixbuf_get_width (icon);
+ icon_height = gdk_pixbuf_get_height (icon);
+ scale = MIN ((double)64 / icon_width, (double)64 / icon_height);
+ scaled_icon = gdk_pixbuf_scale_simple (icon, icon_width * scale, icon_height * scale, GDK_INTERP_NEAREST);
+ g_object_unref (icon);
+ gtk_image_set_from_pixbuf (GTK_IMAGE (general_page->webapp_icon), scaled_icon);
+ g_object_set_data_full (G_OBJECT (general_page->webapp_icon), "ephy-webapp-icon-url",
+ g_strdup (icon_url), g_free);
+ g_object_unref (scaled_icon);
+}
+
+static void
+webapp_icon_chooser_response_cb (GtkNativeDialog *file_chooser,
+ int response,
+ PrefsGeneralPage *general_page)
+{
+ if (response == GTK_RESPONSE_ACCEPT) {
+ char *icon_url;
+
+ icon_url = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_chooser));
+ prefs_general_page_update_webapp_icon (general_page, icon_url);
+ g_free (icon_url);
+ prefs_general_page_save_web_application (general_page);
+ }
+
+ g_object_unref (file_chooser);
+}
+
+static void
+on_webapp_entry_changed (GtkEditable *editable,
+ PrefsGeneralPage *dialog)
+{
+ prefs_general_page_save_web_application (dialog);
+}
+
+static void
+prefs_dialog_response_cb (GtkWidget *widget,
+ int response,
+ PrefsGeneralPage *general_page)
+{
+ if (general_page->webapp_save_id) {
+ g_source_remove (general_page->webapp_save_id);
+ general_page->webapp_save_id = 0;
+ save_web_application (general_page);
+ }
+
+ gtk_widget_destroy (widget);
+}
+
+static void
+on_webapp_icon_button_clicked (GtkWidget *button,
+ PrefsGeneralPage *general_page)
+{
+ GtkFileChooser *file_chooser;
+ GSList *pixbuf_formats, *l;
+ GtkFileFilter *images_filter;
+
+ file_chooser = ephy_create_file_chooser (_("Web Application Icon"),
+ GTK_WIDGET (general_page),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ EPHY_FILE_FILTER_NONE);
+ images_filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (images_filter, _("Supported Image Files"));
+ gtk_file_chooser_add_filter (file_chooser, images_filter);
+
+ pixbuf_formats = gdk_pixbuf_get_formats ();
+ for (l = pixbuf_formats; l; l = g_slist_next (l)) {
+ GdkPixbufFormat *format = (GdkPixbufFormat *)l->data;
+ GtkFileFilter *filter;
+ gchar *name;
+ gchar **mime_types;
+ guint i;
+
+ if (gdk_pixbuf_format_is_disabled (format) || !gdk_pixbuf_format_is_writable (format))
+ continue;
+
+ filter = gtk_file_filter_new ();
+ name = gdk_pixbuf_format_get_description (format);
+ gtk_file_filter_set_name (filter, name);
+ g_free (name);
+
+ mime_types = gdk_pixbuf_format_get_mime_types (format);
+ for (i = 0; mime_types[i] != 0; i++) {
+ gtk_file_filter_add_mime_type (images_filter, mime_types[i]);
+ gtk_file_filter_add_mime_type (filter, mime_types[i]);
+ }
+ g_strfreev (mime_types);
+
+ gtk_file_chooser_add_filter (file_chooser, filter);
+ }
+ g_slist_free (pixbuf_formats);
+
+ g_signal_connect (file_chooser, "response",
+ G_CALLBACK (webapp_icon_chooser_response_cb),
+ general_page);
+
+ gtk_native_dialog_show (GTK_NATIVE_DIALOG (file_chooser));
+}
+
+static void
+custom_homepage_entry_changed (GtkEntry *entry,
+ PrefsGeneralPage *general_page)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (general_page->custom_homepage_radiobutton))) {
+ g_settings_set_string (EPHY_SETTINGS_MAIN, EPHY_PREFS_HOMEPAGE_URL,
+ gtk_entry_get_text (entry));
+ } else if ((gtk_entry_get_text (entry) != NULL) &&
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (general_page->new_tab_homepage_radiobutton))) {
+ g_settings_set_string (EPHY_SETTINGS_MAIN, EPHY_PREFS_HOMEPAGE_URL, gtk_entry_get_text (entry));
+ gtk_widget_set_sensitive (general_page->custom_homepage_entry, TRUE);
+ gtk_widget_grab_focus (general_page->custom_homepage_entry);
+ }
+}
+
+static void
+custom_homepage_entry_icon_released (GtkEntry *entry,
+ GtkEntryIconPosition icon_pos)
+{
+ if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
+ gtk_entry_set_text (entry, "");
+}
+
+static gboolean
+new_tab_homepage_get_mapping (GValue *value,
+ GVariant *variant,
+ gpointer user_data)
+{
+ const char *setting;
+
+ setting = g_variant_get_string (variant, NULL);
+ if (!setting || setting[0] == '\0')
+ g_value_set_boolean (value, TRUE);
+
+ return TRUE;
+}
+
+static GVariant *
+new_tab_homepage_set_mapping (const GValue *value,
+ const GVariantType *expected_type,
+ gpointer user_data)
+{
+ PrefsGeneralPage *general_page = EPHY_PREFS_GENERAL_PAGE (user_data);
+
+ if (!g_value_get_boolean (value))
+ return NULL;
+
+ /* In case the new tab button is pressed while there's text in the custom homepage entry */
+ gtk_entry_set_text (GTK_ENTRY (general_page->custom_homepage_entry), "");
+ gtk_widget_set_sensitive (general_page->custom_homepage_entry, FALSE);
+
+ return g_variant_new_string ("");
+}
+
+static gboolean
+blank_homepage_get_mapping (GValue *value,
+ GVariant *variant,
+ gpointer user_data)
+{
+ const char *setting;
+
+ setting = g_variant_get_string (variant, NULL);
+ if (g_strcmp0 (setting, "about:blank") == 0)
+ g_value_set_boolean (value, TRUE);
+
+ return TRUE;
+}
+
+static GVariant *
+blank_homepage_set_mapping (const GValue *value,
+ const GVariantType *expected_type,
+ gpointer user_data)
+{
+ PrefsGeneralPage *general_page = EPHY_PREFS_GENERAL_PAGE (user_data);
+
+ if (!g_value_get_boolean (value))
+ return NULL;
+
+ gtk_entry_set_text (GTK_ENTRY (general_page->custom_homepage_entry), "");
+
+ return g_variant_new_string ("about:blank");
+}
+
+static gboolean
+custom_homepage_get_mapping (GValue *value,
+ GVariant *variant,
+ gpointer user_data)
+{
+ const char *setting;
+
+ setting = g_variant_get_string (variant, NULL);
+ if (setting && setting[0] != '\0' && g_strcmp0 (setting, "about:blank") != 0)
+ g_value_set_boolean (value, TRUE);
+ return TRUE;
+}
+
+static GVariant *
+custom_homepage_set_mapping (const GValue *value,
+ const GVariantType *expected_type,
+ gpointer user_data)
+{
+ PrefsGeneralPage *general_page = EPHY_PREFS_GENERAL_PAGE (user_data);
+ const char *setting;
+
+ if (!g_value_get_boolean (value)) {
+ gtk_widget_set_sensitive (general_page->custom_homepage_entry, FALSE);
+ gtk_entry_set_text (GTK_ENTRY (general_page->custom_homepage_entry), "");
+ return NULL;
+ }
+
+ gtk_widget_set_sensitive (general_page->custom_homepage_entry, TRUE);
+ gtk_widget_grab_focus (general_page->custom_homepage_entry);
+ setting = gtk_entry_get_text (GTK_ENTRY (general_page->custom_homepage_entry));
+ if (!setting || setting[0] == '\0')
+ return NULL;
+
+ gtk_entry_set_text (GTK_ENTRY (general_page->custom_homepage_entry), setting);
+
+ return g_variant_new_string (setting);
+}
+
+static void
+on_search_engine_dialog_button_clicked (GtkWidget *button,
+ PrefsGeneralPage *general_page)
+{
+ GtkWindow *search_engine_dialog;
+
+ search_engine_dialog = GTK_WINDOW (ephy_search_engine_dialog_new ());
+
+ gtk_window_set_transient_for (search_engine_dialog, GTK_WINDOW (general_page));
+ gtk_window_set_modal (search_engine_dialog, TRUE);
+ gtk_window_present_with_time (search_engine_dialog, gtk_get_current_event_time ());
+}
+
+static void
+on_manage_webapp_additional_urls_button_clicked (GtkWidget *button,
+ PrefsGeneralPage *general_page)
+{
+ EphyWebappAdditionalURLsDialog *urls_dialog;
+
+ urls_dialog = ephy_webapp_additional_urls_dialog_new ();
+ gtk_window_set_transient_for (GTK_WINDOW (urls_dialog), GTK_WINDOW (general_page));
+ gtk_window_set_modal (GTK_WINDOW (urls_dialog), TRUE);
+ gtk_window_present_with_time (GTK_WINDOW (urls_dialog), gtk_get_current_event_time ());
+}
+
+static void
+prefs_general_page_class_init (PrefsGeneralPageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = prefs_general_page_finalize;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gnome/epiphany/gtk/prefs-general-page.ui");
+
+ /* Web Application */
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, webapp_box);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, webapp_icon);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, webapp_url);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, webapp_title);
+
+ /* Homepage */
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, homepage_box);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, new_tab_homepage_radiobutton);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, blank_homepage_radiobutton);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, custom_homepage_radiobutton);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, custom_homepage_entry);
+
+ /* Downloads */
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, download_box);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, ask_on_download_switch);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, download_folder_row);
+
+ /* Search Engines */
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, search_box);
+
+ /* Session */
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, session_box);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, start_in_incognito_mode_switch);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, restore_session_row);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, restore_session_switch);
+
+ /* Browsing */
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, browsing_box);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, enable_smooth_scrolling_switch);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, enable_mouse_gesture_switch);
+
+ /* Languages */
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, lang_group);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, lang_listbox);
+ gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, enable_spell_checking_switch);
+
+ /* Signals */
+ gtk_widget_class_bind_template_callback (widget_class, on_webapp_icon_button_clicked);
+ gtk_widget_class_bind_template_callback (widget_class, on_webapp_entry_changed);
+ gtk_widget_class_bind_template_callback (widget_class, on_manage_webapp_additional_urls_button_clicked);
+ gtk_widget_class_bind_template_callback (widget_class, on_search_engine_dialog_button_clicked);
+}
+
+static const char *css =
+ ".row.drag-icon { "
+ " background: white; "
+ " border: 1px solid black; "
+ "}"
+ ".row.drag-row { "
+ " color: gray; "
+ " background: alpha(gray,0.2); "
+ "}"
+ ".row.drag-row.drag-hover { "
+ " border-top: 1px solid #4e9a06; "
+ " border-bottom: 1px solid #4e9a06; "
+ "}"
+ ".row.drag-hover image, "
+ ".row.drag-hover label { "
+ " color: #4e9a06; "
+ "}"
+ ".row.drag-hover-top {"
+ " border-top: 1px solid #4e9a06; "
+ "}"
+ ".row.drag-hover-bottom {"
+ " border-bottom: 1px solid #4e9a06; "
+ "}"
+;
+
+static void
+init_lang_listbox (PrefsGeneralPage *general_page)
+{
+ char **list = NULL;
+ int i;
+ GtkCssProvider *provider;
+ GtkStyleContext *style_context;
+
+ style_context = gtk_widget_get_style_context (general_page->lang_listbox);
+ gtk_style_context_add_class (style_context, "frame");
+ gtk_list_box_set_header_func (GTK_LIST_BOX (general_page->lang_listbox), hdy_list_box_separator_header,
NULL, NULL);
+
+ provider = gtk_css_provider_new ();
+ gtk_css_provider_load_from_data (provider, css, -1, NULL);
+ gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (provider), 800);
+
+ gtk_drag_dest_set (general_page->lang_listbox, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, entries,
1, GDK_ACTION_MOVE);
+ g_signal_connect (general_page->lang_listbox, "drag-data-received", G_CALLBACK (drag_data_received),
general_page);
+ g_signal_connect (general_page->lang_listbox, "drag-motion", G_CALLBACK (drag_motion), NULL);
+ g_signal_connect (general_page->lang_listbox, "drag-leave", G_CALLBACK (drag_leave), NULL);
+
+ list = g_settings_get_strv (EPHY_SETTINGS_WEB,
+ EPHY_PREFS_WEB_LANGUAGE);
+
+ language_editor_add_function_buttons (general_page);
+
+ /* Fill languages editor */
+ for (i = 0; list[i]; i++) {
+ const char *code = list[i];
+ if (strcmp (code, "system") == 0) {
+ add_system_language_entry (general_page);
+ } else if (code[0] != '\0') {
+ g_autofree char *normalized_locale = normalize_locale (code);
+ if (normalized_locale != NULL) {
+ g_autofree char *language_name = language_for_locale (normalized_locale);
+ if (language_name == NULL)
+ language_name = g_strdup (normalized_locale);
+ language_editor_add (general_page, normalized_locale, language_name);
+ }
+ }
+ }
+}
+
+void
+prefs_general_page_connect_pd_response (PrefsGeneralPage *general_page,
+ PrefsDialog *pd)
+{
+ g_signal_connect (pd, "response", G_CALLBACK (prefs_dialog_response_cb), general_page);
+}
+
+static void
+setup_general_page (PrefsGeneralPage *general_page)
+{
+ GSettings *settings = ephy_settings_get (EPHY_PREFS_SCHEMA);
+ GSettings *web_settings = ephy_settings_get (EPHY_PREFS_WEB_SCHEMA);
+
+ /* ======================================================================== */
+ /* ========================== Web Application ============================= */
+ /* ======================================================================== */
+ if (ephy_embed_shell_get_mode (ephy_embed_shell_get_default ()) == EPHY_EMBED_SHELL_MODE_APPLICATION) {
+ general_page->webapp = ephy_web_application_for_profile_directory (ephy_profile_dir ());
+ g_assert (general_page->webapp);
+ prefs_general_page_update_webapp_icon (general_page, general_page->webapp->icon_url);
+ gtk_entry_set_text (GTK_ENTRY (general_page->webapp_url), general_page->webapp->url);
+ gtk_entry_set_text (GTK_ENTRY (general_page->webapp_title), general_page->webapp->name);
+ }
+
+ /* ======================================================================== */
+ /* ========================== Homepage ==================================== */
+ /* ======================================================================== */
+ g_settings_bind_with_mapping (settings,
+ EPHY_PREFS_HOMEPAGE_URL,
+ general_page->new_tab_homepage_radiobutton,
+ "active",
+ G_SETTINGS_BIND_DEFAULT,
+ new_tab_homepage_get_mapping,
+ new_tab_homepage_set_mapping,
+ general_page,
+ NULL);
+
+ g_settings_bind_with_mapping (settings,
+ EPHY_PREFS_HOMEPAGE_URL,
+ general_page->blank_homepage_radiobutton,
+ "active",
+ G_SETTINGS_BIND_DEFAULT,
+ blank_homepage_get_mapping,
+ blank_homepage_set_mapping,
+ general_page,
+ NULL);
+
+ g_settings_bind_with_mapping (settings,
+ EPHY_PREFS_HOMEPAGE_URL,
+ general_page->custom_homepage_radiobutton,
+ "active",
+ G_SETTINGS_BIND_DEFAULT,
+ custom_homepage_get_mapping,
+ custom_homepage_set_mapping,
+ general_page,
+ NULL);
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (general_page->custom_homepage_radiobutton))) {
+ gtk_widget_set_sensitive (general_page->custom_homepage_entry, TRUE);
+ gtk_entry_set_text (GTK_ENTRY (general_page->custom_homepage_entry),
+ g_settings_get_string (EPHY_SETTINGS_MAIN, EPHY_PREFS_HOMEPAGE_URL));
+ } else {
+ gtk_widget_set_sensitive (general_page->custom_homepage_entry, FALSE);
+ gtk_entry_set_text (GTK_ENTRY (general_page->custom_homepage_entry), "");
+ }
+
+ g_signal_connect (general_page->custom_homepage_entry, "changed",
+ G_CALLBACK (custom_homepage_entry_changed),
+ general_page);
+ g_signal_connect (general_page->custom_homepage_entry, "icon-release",
+ G_CALLBACK (custom_homepage_entry_icon_released),
+ NULL);
+
+ /* ======================================================================== */
+ /* ========================== Downloads =================================== */
+ /* ======================================================================== */
+ if (ephy_is_running_inside_flatpak ())
+ gtk_widget_hide (general_page->download_box);
+ else
+ create_download_path_button (general_page);
+
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_ASK_ON_DOWNLOAD,
+ general_page->ask_on_download_switch,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ /* ======================================================================== */
+ /* ========================== Session ===================================== */
+ /* ======================================================================== */
+ g_settings_bind (settings,
+ EPHY_PREFS_START_IN_INCOGNITO_MODE,
+ general_page->start_in_incognito_mode_switch,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_settings_bind (settings,
+ EPHY_PREFS_START_IN_INCOGNITO_MODE,
+ general_page->restore_session_row,
+ "sensitive",
+ G_SETTINGS_BIND_INVERT_BOOLEAN);
+
+ g_settings_bind_with_mapping (settings,
+ EPHY_PREFS_RESTORE_SESSION_POLICY,
+ general_page->restore_session_switch,
+ "active",
+ G_SETTINGS_BIND_DEFAULT,
+ restore_session_get_mapping,
+ restore_session_set_mapping,
+ NULL, NULL);
+
+ /* ======================================================================== */
+ /* ========================== Browsing ==================================== */
+ /* ======================================================================== */
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_ENABLE_SMOOTH_SCROLLING,
+ general_page->enable_smooth_scrolling_switch,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_ENABLE_MOUSE_GESTURES,
+ general_page->enable_mouse_gesture_switch,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ /* ======================================================================== */
+ /* ========================== Languages =================================== */
+ /* ======================================================================== */
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_ENABLE_SPELL_CHECKING,
+ general_page->enable_spell_checking_switch,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ init_lang_listbox (general_page);
+}
+
+static void
+prefs_general_page_init (PrefsGeneralPage *general_page)
+{
+ EphyEmbedShellMode mode = ephy_embed_shell_get_mode (ephy_embed_shell_get_default ());
+
+ gtk_widget_init_template (GTK_WIDGET (general_page));
+
+ gtk_widget_set_visible (general_page->webapp_box,
+ mode == EPHY_EMBED_SHELL_MODE_APPLICATION);
+ gtk_widget_set_visible (general_page->homepage_box,
+ mode != EPHY_EMBED_SHELL_MODE_APPLICATION);
+ gtk_widget_set_visible (general_page->search_box,
+ mode != EPHY_EMBED_SHELL_MODE_APPLICATION);
+ gtk_widget_set_visible (general_page->session_box,
+ mode != EPHY_EMBED_SHELL_MODE_APPLICATION);
+ gtk_widget_set_visible (general_page->browsing_box,
+ mode != EPHY_EMBED_SHELL_MODE_APPLICATION);
+
+ setup_general_page (general_page);
+}
diff --git a/src/preferences/prefs-general-page.h b/src/preferences/prefs-general-page.h
new file mode 100644
index 000000000..a235c46b6
--- /dev/null
+++ b/src/preferences/prefs-general-page.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of Epiphany.
+ *
+ * Epiphany is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+#define HANDY_USE_UNSTABLE_API
+#include <handy.h>
+
+#include "prefs-dialog.h"
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_PREFS_GENERAL_PAGE (prefs_general_page_get_type ())
+
+G_DECLARE_FINAL_TYPE (PrefsGeneralPage, prefs_general_page, EPHY, PREFS_GENERAL_PAGE, HdyPreferencesPage)
+
+void prefs_general_page_connect_pd_response (PrefsGeneralPage *general_page,
+ PrefsDialog *pd);
+
+G_END_DECLS
diff --git a/src/preferences/prefs-privacy-page.c b/src/preferences/prefs-privacy-page.c
new file mode 100644
index 000000000..53714d5d6
--- /dev/null
+++ b/src/preferences/prefs-privacy-page.c
@@ -0,0 +1,243 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of Epiphany.
+ *
+ * Epiphany is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "prefs-privacy-page.h"
+
+#include "cookies-dialog.h"
+#include "clear-data-dialog.h"
+#include "passwords-dialog.h"
+#include "ephy-settings.h"
+#include "ephy-shell.h"
+
+struct _PrefsPrivacyPage {
+ HdyPreferencesPage parent_instance;
+
+ /* Web Content */
+ GtkWidget *popups_allow_switch;
+ GtkWidget *adblock_allow_switch;
+ GtkWidget *enable_safe_browsing_switch;
+
+ /* Cookies */
+ GtkWidget *always;
+ GtkWidget *no_third_party;
+ GtkWidget *never;
+
+ /* Passwords */
+ GtkWidget *remember_passwords_switch;
+
+ /* Personal Data */
+ GtkWidget *clear_personal_data_button;
+};
+
+G_DEFINE_TYPE (PrefsPrivacyPage, prefs_privacy_page, HDY_TYPE_PREFERENCES_PAGE)
+
+static void
+on_manage_cookies_button_clicked (GtkWidget *button,
+ PrefsPrivacyPage *privacy_page)
+{
+ EphyCookiesDialog *cookies_dialog;
+
+ cookies_dialog = ephy_cookies_dialog_new ();
+
+ gtk_window_set_transient_for (GTK_WINDOW (cookies_dialog), GTK_WINDOW (privacy_page));
+ gtk_window_set_modal (GTK_WINDOW (cookies_dialog), TRUE);
+ gtk_window_present_with_time (GTK_WINDOW (cookies_dialog), gtk_get_current_event_time ());
+}
+
+static void
+on_manage_passwords_button_clicked (GtkWidget *button,
+ PrefsPrivacyPage *privacy_page)
+{
+ EphyPasswordsDialog *passwords_dialog;
+ EphyPasswordManager *password_manager;
+
+ password_manager = ephy_embed_shell_get_password_manager (EPHY_EMBED_SHELL (ephy_shell_get_default ()));
+ passwords_dialog = ephy_passwords_dialog_new (password_manager);
+
+ gtk_window_set_transient_for (GTK_WINDOW (passwords_dialog), GTK_WINDOW (privacy_page));
+ gtk_window_set_modal (GTK_WINDOW (passwords_dialog), TRUE);
+ gtk_window_present_with_time (GTK_WINDOW (passwords_dialog), gtk_get_current_event_time ());
+}
+
+static gboolean
+cookies_get_mapping (GValue *value,
+ GVariant *variant,
+ gpointer user_data)
+{
+ const char *setting;
+ const char *name;
+
+ setting = g_variant_get_string (variant, NULL);
+ name = gtk_buildable_get_name (GTK_BUILDABLE (user_data));
+
+ if (g_strcmp0 (name, "no_third_party") == 0)
+ name = "no-third-party";
+
+ /* If the button name matches the setting, it should be active. */
+ if (g_strcmp0 (name, setting) == 0)
+ g_value_set_boolean (value, TRUE);
+
+ return TRUE;
+}
+
+static GVariant *
+cookies_set_mapping (const GValue *value,
+ const GVariantType *expected_type,
+ gpointer user_data)
+{
+ GVariant *variant = NULL;
+ const char *name;
+
+ /* Don't act unless the button has been activated (turned ON). */
+ if (!g_value_get_boolean (value))
+ return NULL;
+
+ name = gtk_buildable_get_name (GTK_BUILDABLE (user_data));
+ if (g_strcmp0 (name, "no_third_party") == 0)
+ variant = g_variant_new_string ("no-third-party");
+ else
+ variant = g_variant_new_string (name);
+
+ return variant;
+}
+
+static void
+clear_personal_data_button_clicked_cb (GtkWidget *button,
+ PrefsPrivacyPage *privacy_page)
+{
+ ClearDataDialog *clear_dialog;
+
+ clear_dialog = g_object_new (EPHY_TYPE_CLEAR_DATA_DIALOG, NULL);
+ gtk_window_set_transient_for (GTK_WINDOW (clear_dialog), GTK_WINDOW (privacy_page));
+ gtk_window_set_modal (GTK_WINDOW (clear_dialog), TRUE);
+ gtk_window_present_with_time (GTK_WINDOW (clear_dialog), gtk_get_current_event_time ());
+}
+
+static void
+setup_privacy_page (PrefsPrivacyPage *privacy_page)
+{
+ GSettings *web_settings = ephy_settings_get (EPHY_PREFS_WEB_SCHEMA);
+
+ /* ======================================================================== */
+ /* ========================== Web Content ================================= */
+ /* ======================================================================== */
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_ENABLE_ADBLOCK,
+ privacy_page->adblock_allow_switch,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_ENABLE_POPUPS,
+ privacy_page->popups_allow_switch,
+ "active",
+ G_SETTINGS_BIND_INVERT_BOOLEAN);
+
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_ENABLE_SAFE_BROWSING,
+ privacy_page->enable_safe_browsing_switch,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ /* ======================================================================== */
+ /* ========================== Cookies ===================================== */
+ /* ======================================================================== */
+ g_settings_bind_with_mapping (web_settings,
+ EPHY_PREFS_WEB_COOKIES_POLICY,
+ privacy_page->always,
+ "active",
+ G_SETTINGS_BIND_DEFAULT,
+ cookies_get_mapping,
+ cookies_set_mapping,
+ privacy_page->always,
+ NULL);
+
+ g_settings_bind_with_mapping (web_settings,
+ EPHY_PREFS_WEB_COOKIES_POLICY,
+ privacy_page->no_third_party,
+ "active",
+ G_SETTINGS_BIND_DEFAULT,
+ cookies_get_mapping,
+ cookies_set_mapping,
+ privacy_page->no_third_party,
+ NULL);
+
+ g_settings_bind_with_mapping (web_settings,
+ EPHY_PREFS_WEB_COOKIES_POLICY,
+ privacy_page->never,
+ "active",
+ G_SETTINGS_BIND_DEFAULT,
+ cookies_get_mapping,
+ cookies_set_mapping,
+ privacy_page->never,
+ NULL);
+
+ /* ======================================================================== */
+ /* ========================== Passwords =================================== */
+ /* ======================================================================== */
+ g_settings_bind (web_settings,
+ EPHY_PREFS_WEB_REMEMBER_PASSWORDS,
+ privacy_page->remember_passwords_switch,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ /* ======================================================================== */
+ /* ========================== Personal Data =============================== */
+ /* ======================================================================== */
+ g_signal_connect (privacy_page->clear_personal_data_button,
+ "clicked",
+ G_CALLBACK (clear_personal_data_button_clicked_cb),
+ privacy_page);
+}
+
+static void
+prefs_privacy_page_class_init (PrefsPrivacyPageClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gnome/epiphany/gtk/prefs-privacy-page.ui");
+
+ /* Web Content */
+ gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, popups_allow_switch);
+ gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, adblock_allow_switch);
+ gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, enable_safe_browsing_switch);
+
+ /* Cookies */
+ gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, always);
+ gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, no_third_party);
+ gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, never);
+
+ /* Passwords */
+ gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, remember_passwords_switch);
+
+ /* Personal Data */
+ gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, clear_personal_data_button);
+
+ /* Signals */
+ gtk_widget_class_bind_template_callback (widget_class, on_manage_cookies_button_clicked);
+ gtk_widget_class_bind_template_callback (widget_class, on_manage_passwords_button_clicked);
+}
+
+static void
+prefs_privacy_page_init (PrefsPrivacyPage *privacy_page)
+{
+ gtk_widget_init_template (GTK_WIDGET (privacy_page));
+
+ setup_privacy_page (privacy_page);
+}
diff --git a/src/preferences/prefs-privacy-page.h b/src/preferences/prefs-privacy-page.h
new file mode 100644
index 000000000..5e99d603c
--- /dev/null
+++ b/src/preferences/prefs-privacy-page.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of Epiphany.
+ *
+ * Epiphany is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+#define HANDY_USE_UNSTABLE_API
+#include <handy.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_PREFS_PRIVACY_PAGE (prefs_privacy_page_get_type ())
+
+G_DECLARE_FINAL_TYPE (PrefsPrivacyPage, prefs_privacy_page, EPHY, PREFS_PRIVACY_PAGE, HdyPreferencesPage)
+
+G_END_DECLS
diff --git a/src/preferences/prefs-sync-page.c b/src/preferences/prefs-sync-page.c
new file mode 100644
index 000000000..99de12ace
--- /dev/null
+++ b/src/preferences/prefs-sync-page.c
@@ -0,0 +1,790 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of Epiphany.
+ *
+ * Epiphany is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "prefs-sync-page.h"
+
+#include "ephy-debug.h"
+#include "ephy-embed-prefs.h"
+#include "ephy-embed-shell.h"
+#include "ephy-prefs.h"
+#include "ephy-settings.h"
+#include "ephy-shell.h"
+#include "ephy-sync-service.h"
+#include "ephy-sync-utils.h"
+#include "ephy-time-helpers.h"
+#include "synced-tabs-dialog.h"
+
+#define FXA_IFRAME_URL "https://accounts.firefox.com/signin?service=sync&context=fx_desktop_v3";
+
+struct _PrefsSyncPage {
+ HdyPreferencesPage parent_instance;
+
+ GtkWidget *sync_page_box;
+ GtkWidget *sync_firefox_iframe_box;
+ GtkWidget *sync_firefox_iframe_label;
+ GtkWidget *sync_firefox_account_box;
+ GtkWidget *sync_firefox_account_row;
+ GtkWidget *sync_options_box;
+ GtkWidget *sync_bookmarks_checkbutton;
+ GtkWidget *sync_passwords_checkbutton;
+ GtkWidget *sync_history_checkbutton;
+ GtkWidget *sync_open_tabs_checkbutton;
+ GtkWidget *sync_frequency_row;
+ GtkWidget *sync_now_button;
+ GtkWidget *synced_tabs_button;
+ GtkWidget *sync_device_name_entry;
+ GtkWidget *sync_device_name_change_button;
+ GtkWidget *sync_device_name_save_button;
+ GtkWidget *sync_device_name_cancel_button;
+
+ WebKitWebView *fxa_web_view;
+ WebKitUserContentManager *fxa_manager;
+ WebKitUserScript *fxa_script;
+};
+
+G_DEFINE_TYPE (PrefsSyncPage, prefs_sync_page, HDY_TYPE_PREFERENCES_PAGE)
+
+static const guint sync_frequency_minutes[] = { 5, 15, 30, 60 };
+
+static void
+sync_collection_toggled_cb (GtkToggleButton *button,
+ PrefsSyncPage *sync_page)
+{
+ EphySynchronizableManager *manager = NULL;
+ EphyShell *shell = ephy_shell_get_default ();
+ EphySyncService *service = ephy_shell_get_sync_service (shell);
+
+ if (GTK_WIDGET (button) == sync_page->sync_bookmarks_checkbutton) {
+ manager = EPHY_SYNCHRONIZABLE_MANAGER (ephy_shell_get_bookmarks_manager (shell));
+ } else if (GTK_WIDGET (button) == sync_page->sync_passwords_checkbutton) {
+ manager = EPHY_SYNCHRONIZABLE_MANAGER (ephy_embed_shell_get_password_manager (EPHY_EMBED_SHELL (shell)));
+ } else if (GTK_WIDGET (button) == sync_page->sync_history_checkbutton) {
+ manager = EPHY_SYNCHRONIZABLE_MANAGER (ephy_shell_get_history_manager (shell));
+ } else if (GTK_WIDGET (button) == sync_page->sync_open_tabs_checkbutton) {
+ manager = EPHY_SYNCHRONIZABLE_MANAGER (ephy_shell_get_open_tabs_manager (shell));
+ ephy_open_tabs_manager_clear_cache (EPHY_OPEN_TABS_MANAGER (manager));
+ } else {
+ g_assert_not_reached ();
+ }
+
+ if (gtk_toggle_button_get_active (button)) {
+ ephy_sync_service_register_manager (service, manager);
+ } else {
+ ephy_sync_service_unregister_manager (service, manager);
+ ephy_synchronizable_manager_set_is_initial_sync (manager, TRUE);
+ }
+}
+
+static void
+sync_set_last_sync_time (PrefsSyncPage *sync_page)
+{
+ gint64 sync_time = ephy_sync_utils_get_sync_time ();
+
+ if (sync_time) {
+ char *time = ephy_time_helpers_utf_friendly_time (sync_time);
+ /* Translators: the %s refers to the time at which the last sync was made.
+ * For example: Today 04:34 PM, Sun 11:25 AM, May 31 06:41 PM.
+ */
+ char *text = g_strdup_printf (_("Last synchronized: %s"), time);
+
+ hdy_action_row_set_subtitle (HDY_ACTION_ROW (sync_page->sync_firefox_account_row), text);
+
+ g_free (text);
+ g_free (time);
+ }
+}
+
+static void
+sync_finished_cb (EphySyncService *service,
+ PrefsSyncPage *sync_page)
+{
+ g_assert (EPHY_IS_SYNC_SERVICE (service));
+ g_assert (EPHY_IS_PREFS_SYNC_PAGE (sync_page));
+
+ gtk_widget_set_sensitive (sync_page->sync_now_button, TRUE);
+ sync_set_last_sync_time (sync_page);
+}
+
+static void
+sync_sign_in_details_show (PrefsSyncPage *sync_page,
+ const char *text)
+{
+ char *message;
+
+ g_assert (EPHY_IS_PREFS_SYNC_PAGE (sync_page));
+
+ message = g_strdup_printf ("<span fgcolor='#e6780b'>%s</span>", text);
+ gtk_label_set_markup (GTK_LABEL (sync_page->sync_firefox_iframe_label), message);
+ gtk_widget_set_visible (sync_page->sync_firefox_iframe_label, TRUE);
+
+ g_free (message);
+}
+
+static void
+sync_sign_in_error_cb (EphySyncService *service,
+ const char *error,
+ PrefsSyncPage *sync_page)
+{
+ g_assert (EPHY_IS_SYNC_SERVICE (service));
+ g_assert (EPHY_IS_PREFS_SYNC_PAGE (sync_page));
+
+ /* Display the error message and reload the iframe. */
+ sync_sign_in_details_show (sync_page, error);
+ webkit_web_view_load_uri (sync_page->fxa_web_view, FXA_IFRAME_URL);
+}
+
+static void
+sync_secrets_store_finished_cb (EphySyncService *service,
+ GError *error,
+ PrefsSyncPage *sync_page)
+{
+ g_assert (EPHY_IS_SYNC_SERVICE (service));
+ g_assert (EPHY_IS_PREFS_SYNC_PAGE (sync_page));
+
+ if (!error) {
+ hdy_action_row_set_title (HDY_ACTION_ROW (sync_page->sync_firefox_account_row),
+ ephy_sync_utils_get_sync_user ());
+ gtk_widget_hide (sync_page->sync_page_box);
+ gtk_widget_show (sync_page->sync_firefox_account_box);
+ gtk_widget_show (sync_page->sync_options_box);
+ } else {
+ /* Display the error message and reload the iframe. */
+ sync_sign_in_details_show (sync_page, error->message);
+ webkit_web_view_load_uri (sync_page->fxa_web_view, FXA_IFRAME_URL);
+ }
+}
+
+static void
+sync_message_to_fxa_content (PrefsSyncPage *sync_page,
+ const char *web_channel_id,
+ const char *command,
+ const char *message_id,
+ JsonObject *data)
+{
+ JsonNode *node;
+ JsonObject *detail;
+ JsonObject *message;
+ char *detail_str;
+ char *script;
+ const char *type;
+
+ g_assert (EPHY_IS_PREFS_SYNC_PAGE (sync_page));
+ g_assert (web_channel_id);
+ g_assert (command);
+ g_assert (message_id);
+ g_assert (data);
+
+ message = json_object_new ();
+ json_object_set_string_member (message, "command", command);
+ json_object_set_string_member (message, "messageId", message_id);
+ json_object_set_object_member (message, "data", json_object_ref (data));
+ detail = json_object_new ();
+ json_object_set_string_member (detail, "id", web_channel_id);
+ json_object_set_object_member (detail, "message", message);
+ node = json_node_new (JSON_NODE_OBJECT);
+ json_node_set_object (node, detail);
+
+ type = "WebChannelMessageToContent";
+ detail_str = json_to_string (node, FALSE);
+ script = g_strdup_printf ("let e = new window.CustomEvent(\"%s\", {detail: %s});"
+ "window.dispatchEvent(e);",
+ type, detail_str);
+
+ /* We don't expect any response from the server. */
+ webkit_web_view_run_javascript (sync_page->fxa_web_view, script, NULL, NULL, NULL);
+
+ g_free (script);
+ g_free (detail_str);
+ json_object_unref (detail);
+ json_node_unref (node);
+}
+
+static gboolean
+sync_parse_message_from_fxa_content (const char *message,
+ char **web_channel_id,
+ char **message_id,
+ char **command,
+ JsonObject **data,
+ char **error_msg)
+{
+ JsonNode *node;
+ JsonObject *object;
+ JsonObject *detail;
+ JsonObject *msg;
+ JsonObject *msg_data = NULL;
+ const char *type;
+ const char *channel_id;
+ const char *cmd;
+ const char *error = NULL;
+ gboolean success = FALSE;
+
+ g_assert (message);
+ g_assert (web_channel_id);
+ g_assert (message_id);
+ g_assert (command);
+ g_assert (data);
+ g_assert (error_msg);
+
+ /* Expected message format is:
+ * {
+ * type: "WebChannelMessageToChrome",
+ * detail: {
+ * id: <id> (string, the id of the WebChannel),
+ * message: {
+ * messageId: <messageId> (optional string, the message id),
+ * command: <command> (string, the message command),
+ * data: <data> (optional JSON object, the message data)
+ * }
+ * }
+ * }
+ */
+
+ node = json_from_string (message, NULL);
+ if (!node) {
+ error = "Message is not a valid JSON";
+ goto out_error;
+ }
+ object = json_node_get_object (node);
+ if (!object) {
+ error = "Message is not a JSON object";
+ goto out_error;
+ }
+ type = json_object_get_string_member (object, "type");
+ if (!type) {
+ error = "Message has missing or invalid 'type' member";
+ goto out_error;
+ } else if (strcmp (type, "WebChannelMessageToChrome")) {
+ error = "Message type is not WebChannelMessageToChrome";
+ goto out_error;
+ }
+ detail = json_object_get_object_member (object, "detail");
+ if (!detail) {
+ error = "Message has missing or invalid 'detail' member";
+ goto out_error;
+ }
+ channel_id = json_object_get_string_member (detail, "id");
+ if (!channel_id) {
+ error = "'Detail' object has missing or invalid 'id' member";
+ goto out_error;
+ }
+ msg = json_object_get_object_member (detail, "message");
+ if (!msg) {
+ error = "'Detail' object has missing or invalid 'message' member";
+ goto out_error;
+ }
+ cmd = json_object_get_string_member (msg, "command");
+ if (!cmd) {
+ error = "'Message' object has missing or invalid 'command' member";
+ goto out_error;
+ }
+
+ *web_channel_id = g_strdup (channel_id);
+ *command = g_strdup (cmd);
+ *message_id = json_object_has_member (msg, "messageId") ?
+ g_strdup (json_object_get_string_member (msg, "messageId")) :
+ NULL;
+ if (json_object_has_member (msg, "data"))
+ msg_data = json_object_get_object_member (msg, "data");
+ *data = msg_data ? json_object_ref (msg_data) : NULL;
+
+ success = TRUE;
+ *error_msg = NULL;
+ goto out_no_error;
+
+out_error:
+ *web_channel_id = NULL;
+ *command = NULL;
+ *message_id = NULL;
+ *error_msg = g_strdup (error);
+
+out_no_error:
+ json_node_unref (node);
+
+ return success;
+}
+
+static void
+sync_message_from_fxa_content_cb (WebKitUserContentManager *manager,
+ WebKitJavascriptResult *result,
+ PrefsSyncPage *sync_page)
+{
+ JsonObject *data = NULL;
+ char *message = NULL;
+ char *web_channel_id = NULL;
+ char *message_id = NULL;
+ char *command = NULL;
+ char *error_msg = NULL;
+ gboolean is_error = FALSE;
+
+ message = jsc_value_to_string (webkit_javascript_result_get_js_value (result));
+ if (!message) {
+ g_warning ("Failed to get JavaScript result as string");
+ is_error = TRUE;
+ goto out;
+ }
+
+ if (!sync_parse_message_from_fxa_content (message, &web_channel_id,
+ &message_id, &command,
+ &data, &error_msg)) {
+ g_warning ("Failed to parse message from FxA Content Server: %s", error_msg);
+ is_error = TRUE;
+ goto out;
+ }
+
+ LOG ("WebChannelMessageToChrome: received %s command", command);
+
+ if (!g_strcmp0 (command, "fxaccounts:can_link_account")) {
+ /* Confirm a relink. Respond with {ok: true}. */
+ JsonObject *response = json_object_new ();
+ json_object_set_boolean_member (response, "ok", TRUE);
+ sync_message_to_fxa_content (sync_page, web_channel_id, command, message_id, response);
+ json_object_unref (response);
+ } else if (!g_strcmp0 (command, "fxaccounts:login")) {
+ /* Extract sync tokens and pass them to the sync service. */
+ const char *email = json_object_get_string_member (data, "email");
+ const char *uid = json_object_get_string_member (data, "uid");
+ const char *session_token = json_object_get_string_member (data, "sessionToken");
+ const char *key_fetch_token = json_object_get_string_member (data, "keyFetchToken");
+ const char *unwrap_kb = json_object_get_string_member (data, "unwrapBKey");
+
+ if (!email || !uid || !session_token || !key_fetch_token || !unwrap_kb) {
+ g_warning ("Message data has missing or invalid members");
+ is_error = TRUE;
+ goto out;
+ }
+ if (!json_object_has_member (data, "verified") ||
+ !JSON_NODE_HOLDS_VALUE (json_object_get_member (data, "verified"))) {
+ g_warning ("Message data has missing or invalid 'verified' member");
+ is_error = TRUE;
+ goto out;
+ }
+
+ ephy_sync_service_sign_in (ephy_shell_get_sync_service (ephy_shell_get_default ()),
+ email, uid, session_token, key_fetch_token, unwrap_kb);
+ }
+
+out:
+ if (data)
+ json_object_unref (data);
+ g_free (message);
+ g_free (web_channel_id);
+ g_free (message_id);
+ g_free (command);
+ g_free (error_msg);
+
+ if (is_error) {
+ sync_sign_in_details_show (sync_page, _("Something went wrong, please try again later."));
+ webkit_web_view_load_uri (sync_page->fxa_web_view, FXA_IFRAME_URL);
+ }
+}
+
+static void
+sync_open_webmail_clicked_cb (WebKitUserContentManager *manager,
+ WebKitJavascriptResult *result,
+ PrefsSyncPage *sync_page)
+{
+ EphyShell *shell;
+ EphyEmbed *embed;
+ GtkWindow *window;
+ GtkWidget *prefs_dialog;
+ char *url;
+
+ url = jsc_value_to_string (webkit_javascript_result_get_js_value (result));
+ if (url) {
+ /* Open a new tab to the webmail URL. */
+ shell = ephy_shell_get_default ();
+ window = gtk_application_get_active_window (GTK_APPLICATION (shell));
+ embed = ephy_shell_new_tab (shell, EPHY_WINDOW (window),
+ NULL, EPHY_NEW_TAB_JUMP);
+ ephy_web_view_load_url (ephy_embed_get_web_view (embed), url);
+
+ /* Close the preferences dialog. */
+ prefs_dialog = gtk_widget_get_toplevel (GTK_WIDGET (sync_page));
+ gtk_widget_destroy (GTK_WIDGET (prefs_dialog));
+
+ g_free (url);
+ }
+}
+
+static void
+sync_setup_firefox_iframe (PrefsSyncPage *sync_page)
+{
+ EphyEmbedShell *shell;
+ WebKitWebsiteDataManager *manager;
+ WebKitWebContext *embed_context;
+ WebKitWebContext *sync_context;
+ GtkWidget *frame;
+ const char *script;
+
+ if (!sync_page->fxa_web_view) {
+ script =
+ /* Handle sign-in messages from the FxA content server. */
+ "function handleToChromeMessage(event) {"
+ " let e = JSON.stringify({type: event.type, detail: event.detail});"
+ " window.webkit.messageHandlers.toChromeMessageHandler.postMessage(e);"
+ "};"
+ "window.addEventListener('WebChannelMessageToChrome', handleToChromeMessage);"
+ /* Handle open-webmail click event. */
+ "function handleOpenWebmailClick(event) {"
+ " if (event.target.id == 'open-webmail' && event.target.hasAttribute('href'))"
+ "
window.webkit.messageHandlers.openWebmailClickHandler.postMessage(event.target.getAttribute('href'));"
+ "};"
+ "var stage = document.getElementById('stage');"
+ "if (stage)"
+ " stage.addEventListener('click', handleOpenWebmailClick);";
+
+ sync_page->fxa_script = webkit_user_script_new (script,
+ WEBKIT_USER_CONTENT_INJECT_TOP_FRAME,
+ WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END,
+ NULL, NULL);
+ sync_page->fxa_manager = webkit_user_content_manager_new ();
+ webkit_user_content_manager_add_script (sync_page->fxa_manager, sync_page->fxa_script);
+ g_signal_connect (sync_page->fxa_manager,
+ "script-message-received::toChromeMessageHandler",
+ G_CALLBACK (sync_message_from_fxa_content_cb),
+ sync_page);
+ g_signal_connect (sync_page->fxa_manager,
+ "script-message-received::openWebmailClickHandler",
+ G_CALLBACK (sync_open_webmail_clicked_cb),
+ sync_page);
+ webkit_user_content_manager_register_script_message_handler (sync_page->fxa_manager,
+ "toChromeMessageHandler");
+ webkit_user_content_manager_register_script_message_handler (sync_page->fxa_manager,
+ "openWebmailClickHandler");
+
+ shell = ephy_embed_shell_get_default ();
+ embed_context = ephy_embed_shell_get_web_context (shell);
+ manager = webkit_web_context_get_website_data_manager (embed_context);
+ sync_context = webkit_web_context_new_with_website_data_manager (manager);
+ webkit_web_context_set_preferred_languages (sync_context,
+ g_object_get_data (G_OBJECT (embed_context),
"preferred-languages"));
+
+ sync_page->fxa_web_view = WEBKIT_WEB_VIEW (g_object_new (WEBKIT_TYPE_WEB_VIEW,
+ "user-content-manager", sync_page->fxa_manager,
+ "settings", ephy_embed_prefs_get_settings (),
+ "web-context", sync_context,
+ NULL));
+ gtk_widget_set_vexpand (GTK_WIDGET (sync_page->fxa_web_view), TRUE);
+ gtk_widget_set_visible (GTK_WIDGET (sync_page->fxa_web_view), TRUE);
+ frame = gtk_frame_new (NULL);
+ gtk_widget_set_visible (frame, TRUE);
+ gtk_container_add (GTK_CONTAINER (frame),
+ GTK_WIDGET (sync_page->fxa_web_view));
+ gtk_box_pack_start (GTK_BOX (sync_page->sync_firefox_iframe_box),
+ frame,
+ TRUE, TRUE, 0);
+
+ g_object_unref (sync_context);
+ }
+
+ webkit_web_view_load_uri (sync_page->fxa_web_view, FXA_IFRAME_URL);
+ gtk_widget_set_visible (sync_page->sync_firefox_iframe_label, FALSE);
+}
+
+static void
+on_sync_sign_out_button_clicked (GtkWidget *button,
+ PrefsSyncPage *sync_page)
+{
+ EphySyncService *service = ephy_shell_get_sync_service (ephy_shell_get_default ());
+
+ ephy_sync_service_sign_out (service);
+
+ /* Show Firefox Accounts iframe. */
+ sync_setup_firefox_iframe (sync_page);
+ gtk_widget_hide (sync_page->sync_firefox_account_box);
+ gtk_widget_hide (sync_page->sync_options_box);
+ gtk_widget_show (sync_page->sync_page_box);
+ hdy_action_row_set_subtitle (HDY_ACTION_ROW (sync_page->sync_firefox_account_row), NULL);
+}
+
+static void
+on_sync_sync_now_button_clicked (GtkWidget *button,
+ PrefsSyncPage *sync_page)
+{
+ EphySyncService *service = ephy_shell_get_sync_service (ephy_shell_get_default ());
+
+ gtk_widget_set_sensitive (button, FALSE);
+ ephy_sync_service_sync (service);
+}
+
+static void
+on_sync_synced_tabs_button_clicked (GtkWidget *button,
+ PrefsSyncPage *sync_page)
+{
+ EphyOpenTabsManager *manager;
+ SyncedTabsDialog *synced_tabs_dialog;
+
+ manager = ephy_shell_get_open_tabs_manager (ephy_shell_get_default ());
+ synced_tabs_dialog = synced_tabs_dialog_new (manager);
+ gtk_window_set_transient_for (GTK_WINDOW (synced_tabs_dialog), GTK_WINDOW (sync_page));
+ gtk_window_set_modal (GTK_WINDOW (synced_tabs_dialog), TRUE);
+ gtk_window_present_with_time (GTK_WINDOW (synced_tabs_dialog), gtk_get_current_event_time ());
+}
+
+static void
+on_sync_device_name_change_button_clicked (GtkWidget *button,
+ PrefsSyncPage *sync_page)
+{
+ gtk_widget_set_sensitive (GTK_WIDGET (sync_page->sync_device_name_entry), TRUE);
+ gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_change_button), FALSE);
+ gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_save_button), TRUE);
+ gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_cancel_button), TRUE);
+}
+
+static void
+on_sync_device_name_save_button_clicked (GtkWidget *button,
+ PrefsSyncPage *sync_page)
+{
+ EphySyncService *service = ephy_shell_get_sync_service (ephy_shell_get_default ());
+ const char *text;
+
+ text = gtk_entry_get_text (GTK_ENTRY (sync_page->sync_device_name_entry));
+ if (!g_strcmp0 (text, "")) {
+ char *name = ephy_sync_utils_get_device_name ();
+ gtk_entry_set_text (GTK_ENTRY (sync_page->sync_device_name_entry), name);
+ g_free (name);
+ } else {
+ ephy_sync_service_update_device_name (service, text);
+ }
+
+ gtk_widget_set_sensitive (GTK_WIDGET (sync_page->sync_device_name_entry), FALSE);
+ gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_change_button), TRUE);
+ gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_save_button), FALSE);
+ gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_cancel_button), FALSE);
+}
+
+static void
+on_sync_device_name_cancel_button_clicked (GtkWidget *button,
+ PrefsSyncPage *sync_page)
+{
+ char *name;
+
+ name = ephy_sync_utils_get_device_name ();
+ gtk_entry_set_text (GTK_ENTRY (sync_page->sync_device_name_entry), name);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (sync_page->sync_device_name_entry), FALSE);
+ gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_change_button), TRUE);
+ gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_save_button), FALSE);
+ gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_cancel_button), FALSE);
+
+ g_free (name);
+}
+
+static void
+prefs_sync_page_finalize (GObject *object)
+{
+ PrefsSyncPage *sync_page = EPHY_PREFS_SYNC_PAGE (object);
+
+ if (sync_page->fxa_web_view != NULL) {
+ webkit_user_content_manager_unregister_script_message_handler (sync_page->fxa_manager,
+ "toChromeMessageHandler");
+ webkit_user_content_manager_unregister_script_message_handler (sync_page->fxa_manager,
+ "openWebmailClickHandler");
+ webkit_user_script_unref (sync_page->fxa_script);
+ g_object_unref (sync_page->fxa_manager);
+ }
+
+ G_OBJECT_CLASS (prefs_sync_page_parent_class)->finalize (object);
+}
+
+static void
+prefs_sync_page_class_init (PrefsSyncPageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gnome/epiphany/gtk/prefs-sync-page.ui");
+
+ object_class->finalize = prefs_sync_page_finalize;
+
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_page_box);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_firefox_iframe_box);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_firefox_iframe_label);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_firefox_account_box);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_firefox_account_row);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_options_box);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_bookmarks_checkbutton);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_passwords_checkbutton);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_history_checkbutton);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_open_tabs_checkbutton);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_frequency_row);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_now_button);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, synced_tabs_button);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_device_name_entry);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_device_name_change_button);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_device_name_save_button);
+ gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_device_name_cancel_button);
+
+ gtk_widget_class_bind_template_callback (widget_class, on_sync_sign_out_button_clicked);
+ gtk_widget_class_bind_template_callback (widget_class, on_sync_sync_now_button_clicked);
+ gtk_widget_class_bind_template_callback (widget_class, on_sync_synced_tabs_button_clicked);
+ gtk_widget_class_bind_template_callback (widget_class, on_sync_device_name_change_button_clicked);
+ gtk_widget_class_bind_template_callback (widget_class, on_sync_device_name_save_button_clicked);
+ gtk_widget_class_bind_template_callback (widget_class, on_sync_device_name_cancel_button_clicked);
+}
+
+static gboolean
+sync_frequency_get_mapping (GValue *value,
+ GVariant *variant,
+ gpointer user_data)
+{
+ uint minutes = g_variant_get_uint32 (variant);
+
+ for (gint i = 0; i < (gint)G_N_ELEMENTS (sync_frequency_minutes); i++) {
+ if (sync_frequency_minutes[i] != minutes)
+ continue;
+
+ g_value_set_int (value, i);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GVariant *
+sync_frequency_set_mapping (const GValue *value,
+ const GVariantType *expected_type,
+ gpointer user_data)
+{
+ gint i = g_value_get_int (value);
+
+ if (i >= (gint)G_N_ELEMENTS (sync_frequency_minutes))
+ return NULL;
+
+ return g_variant_new_uint32 (sync_frequency_minutes[i]);
+}
+
+static GListModel *
+create_sync_frequency_minutes_model ()
+{
+ GListStore *list_store = g_list_store_new (HDY_TYPE_VALUE_OBJECT);
+ HdyValueObject *obj;
+ g_auto (GValue) value = G_VALUE_INIT;
+ guint i;
+
+ g_value_init (&value, G_TYPE_UINT);
+
+ for (i = 0; i < G_N_ELEMENTS (sync_frequency_minutes); i++) {
+ g_value_set_uint (&value, sync_frequency_minutes[i]);
+ obj = hdy_value_object_new (&value);
+ g_list_store_insert (list_store, i, obj);
+ g_clear_object (&obj);
+ }
+
+ return G_LIST_MODEL (list_store);
+}
+
+static gchar *
+get_sync_frequency_minutes_name (HdyValueObject *value)
+{
+ return g_strdup_printf ("%u min", g_value_get_uint (hdy_value_object_get_value (value)));
+}
+
+void
+prefs_sync_page_setup (PrefsSyncPage *sync_page)
+{
+ EphySyncService *service = ephy_shell_get_sync_service (ephy_shell_get_default ());
+ GSettings *sync_settings = ephy_settings_get (EPHY_PREFS_SYNC_SCHEMA);
+ char *user = ephy_sync_utils_get_sync_user ();
+ char *name = ephy_sync_utils_get_device_name ();
+ g_autoptr (GListModel) sync_frequency_minutes_model = create_sync_frequency_minutes_model ();
+
+ gtk_entry_set_text (GTK_ENTRY (sync_page->sync_device_name_entry), name);
+
+ if (!user) {
+ sync_setup_firefox_iframe (sync_page);
+ gtk_widget_hide (sync_page->sync_firefox_account_box);
+ gtk_widget_hide (sync_page->sync_options_box);
+ } else {
+ sync_set_last_sync_time (sync_page);
+ hdy_action_row_set_title (HDY_ACTION_ROW (sync_page->sync_firefox_account_row), user);
+ gtk_widget_hide (sync_page->sync_page_box);
+ }
+
+ g_settings_bind (sync_settings,
+ EPHY_PREFS_SYNC_BOOKMARKS_ENABLED,
+ sync_page->sync_bookmarks_checkbutton,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+ g_settings_bind (sync_settings,
+ EPHY_PREFS_SYNC_PASSWORDS_ENABLED,
+ sync_page->sync_passwords_checkbutton,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+ g_settings_bind (sync_settings,
+ EPHY_PREFS_SYNC_HISTORY_ENABLED,
+ sync_page->sync_history_checkbutton,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+ g_settings_bind (sync_settings,
+ EPHY_PREFS_SYNC_OPEN_TABS_ENABLED,
+ sync_page->sync_open_tabs_checkbutton,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+
+ hdy_combo_row_bind_name_model (HDY_COMBO_ROW (sync_page->sync_frequency_row),
+ sync_frequency_minutes_model,
+ (HdyComboRowGetNameFunc)get_sync_frequency_minutes_name,
+ NULL,
+ NULL);
+ g_settings_bind_with_mapping (sync_settings,
+ EPHY_PREFS_SYNC_FREQUENCY,
+ sync_page->sync_frequency_row,
+ "selected-index",
+ G_SETTINGS_BIND_DEFAULT,
+ sync_frequency_get_mapping,
+ sync_frequency_set_mapping,
+ NULL, NULL);
+
+ g_object_bind_property (sync_page->sync_open_tabs_checkbutton, "active",
+ sync_page->synced_tabs_button, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ g_signal_connect_object (service, "sync-secrets-store-finished",
+ G_CALLBACK (sync_secrets_store_finished_cb),
+ sync_page, 0);
+ g_signal_connect_object (service, "sync-sign-in-error",
+ G_CALLBACK (sync_sign_in_error_cb),
+ sync_page, 0);
+ g_signal_connect_object (service, "sync-finished",
+ G_CALLBACK (sync_finished_cb),
+ sync_page, 0);
+ g_signal_connect_object (sync_page->sync_bookmarks_checkbutton, "toggled",
+ G_CALLBACK (sync_collection_toggled_cb),
+ sync_page, 0);
+ g_signal_connect_object (sync_page->sync_passwords_checkbutton, "toggled",
+ G_CALLBACK (sync_collection_toggled_cb),
+ sync_page, 0);
+ g_signal_connect_object (sync_page->sync_history_checkbutton, "toggled",
+ G_CALLBACK (sync_collection_toggled_cb),
+ sync_page, 0);
+ g_signal_connect_object (sync_page->sync_open_tabs_checkbutton, "toggled",
+ G_CALLBACK (sync_collection_toggled_cb),
+ sync_page, 0);
+
+ g_free (user);
+ g_free (name);
+}
+
+static void
+prefs_sync_page_init (PrefsSyncPage *sync_page)
+{
+ gtk_widget_init_template (GTK_WIDGET (sync_page));
+}
diff --git a/src/preferences/prefs-sync-page.h b/src/preferences/prefs-sync-page.h
new file mode 100644
index 000000000..b7c5c2846
--- /dev/null
+++ b/src/preferences/prefs-sync-page.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of Epiphany.
+ *
+ * Epiphany is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+#define HANDY_USE_UNSTABLE_API
+#include <handy.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_PREFS_SYNC_PAGE (prefs_sync_page_get_type ())
+
+G_DECLARE_FINAL_TYPE (PrefsSyncPage, prefs_sync_page, EPHY, PREFS_SYNC_PAGE, HdyPreferencesPage)
+
+void prefs_sync_page_setup (PrefsSyncPage *sync_page);
+
+G_END_DECLS
diff --git a/src/synced-tabs-dialog.c b/src/preferences/synced-tabs-dialog.c
similarity index 100%
rename from src/synced-tabs-dialog.c
rename to src/preferences/synced-tabs-dialog.c
diff --git a/src/synced-tabs-dialog.h b/src/preferences/synced-tabs-dialog.h
similarity index 100%
rename from src/synced-tabs-dialog.h
rename to src/preferences/synced-tabs-dialog.h
diff --git a/src/webapp-additional-urls-dialog.c b/src/preferences/webapp-additional-urls-dialog.c
similarity index 100%
rename from src/webapp-additional-urls-dialog.c
rename to src/preferences/webapp-additional-urls-dialog.c
diff --git a/src/webapp-additional-urls-dialog.h b/src/preferences/webapp-additional-urls-dialog.h
similarity index 100%
rename from src/webapp-additional-urls-dialog.h
rename to src/preferences/webapp-additional-urls-dialog.h
diff --git a/src/resources/epiphany.gresource.xml b/src/resources/epiphany.gresource.xml
index 12da60dd5..de5e2b5dc 100644
--- a/src/resources/epiphany.gresource.xml
+++ b/src/resources/epiphany.gresource.xml
@@ -31,8 +31,12 @@
<file preprocess="xml-stripblanks" compressed="true">gtk/pages-popover.ui</file>
<file preprocess="xml-stripblanks" compressed="true">gtk/pages-view.ui</file>
<file preprocess="xml-stripblanks" compressed="true">gtk/passwords-dialog.ui</file>
+ <file preprocess="xml-stripblanks" compressed="true">gtk/prefs-appearance-page.ui</file>
<file preprocess="xml-stripblanks" compressed="true">gtk/prefs-dialog.ui</file>
+ <file preprocess="xml-stripblanks" compressed="true">gtk/prefs-general-page.ui</file>
<file preprocess="xml-stripblanks" compressed="true">gtk/prefs-lang-dialog.ui</file>
+ <file preprocess="xml-stripblanks" compressed="true">gtk/prefs-privacy-page.ui</file>
+ <file preprocess="xml-stripblanks" compressed="true">gtk/prefs-sync-page.ui</file>
<file preprocess="xml-stripblanks" compressed="true">gtk/search-engine-dialog.ui</file>
<file preprocess="xml-stripblanks" compressed="true">gtk/synced-tabs-dialog.ui</file>
<file preprocess="xml-stripblanks" compressed="true">gtk/shortcuts-dialog.ui</file>
diff --git a/src/resources/gtk/prefs-appearance-page.ui b/src/resources/gtk/prefs-appearance-page.ui
new file mode 100644
index 000000000..d4b57d1d8
--- /dev/null
+++ b/src/resources/gtk/prefs-appearance-page.ui
@@ -0,0 +1,216 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.0"/>
+ <template class="PrefsAppearancePage" parent="HdyPreferencesPage">
+ <property name="icon_name">document-edit-symbolic</property>
+ <property name="title" translatable="yes">Appearance</property>
+ <property name="visible">True</property>
+ <style>
+ <class name="background"/>
+ </style>
+ <child>
+ <object class="HdyPreferencesGroup">
+ <property name="title" translatable="yes">Fonts</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyExpanderRow" id="use_gnome_fonts_row">
+ <property name="show_enable_switch">True</property>
+ <property name="title" translatable="yes">Use Custom Fonts</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkSeparator">
+ <property name="margin-start">24</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBox" id="use_custom_fonts_list">
+ <property name="margin-start">24</property>
+ <property name="selection_mode">none</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">Sans serif font</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkFontButton" id="sans_fontbutton">
+ <property name="font">Sans 12</property>
+ <property name="use-font">True</property>
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">Serif font</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkFontButton" id="serif_fontbutton">
+ <property name="font">Sans 12</property>
+ <property name="use-font">True</property>
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">Monospace font</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkFontButton" id="mono_fontbutton">
+ <property name="font">Sans 12</property>
+ <property name="use-font">True</property>
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyPreferencesGroup" id="reader_mode_box">
+ <property name="title" translatable="yes">Reader Mode</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyComboRow" id="reader_mode_font_style">
+ <property name="title" translatable="yes">Font Style</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="HdyComboRow" id="reader_mode_color_scheme">
+ <property name="title" translatable="yes">Color Scheme</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="HdyPreferencesGroup">
+ <property name="title" translatable="yes">Style</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">css_switch</property>
+ <property name="title" translatable="yes">Use Custom Stylesheet</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkButton" id="css_edit_button">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon_name">document-edit-symbolic</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkSeparator">
+ <property name="margin_bottom">8</property>
+ <property name="margin_top">8</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkSwitch" id="css_switch">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">js_switch</property>
+ <property name="title" translatable="yes">Use Custom JavaScript</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkButton" id="js_edit_button">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon_name">document-edit-symbolic</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkSeparator">
+ <property name="margin_bottom">8</property>
+ <property name="margin_top">8</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkSwitch" id="js_switch">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">Default zoom level</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkSpinButton" id="default_zoom_spin_button">
+ <property name="adjustment">zoom_adjustment</property>
+ <property name="can_focus">True</property>
+ <property name="input_purpose">number</property>
+ <property name="max_width_chars">5</property>
+ <property name="valign">center</property>
+ <property name="value">100</property>
+ <property name="visible">True</property>
+ <signal name="output" handler="on_default_zoom_spin_button_output"/>
+ <signal name="value-changed" handler="on_default_zoom_spin_button_value_changed"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkSizeGroup">
+ <property name="mode">horizontal</property>
+ <widgets>
+ <widget name="sans_fontbutton"/>
+ <widget name="serif_fontbutton"/>
+ <widget name="mono_fontbutton"/>
+ </widgets>
+ </object>
+ <object class="GtkAdjustment" id="zoom_adjustment">
+ <property name="lower">33</property>
+ <property name="upper">300</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+</interface>
diff --git a/src/resources/gtk/prefs-dialog.ui b/src/resources/gtk/prefs-dialog.ui
index fbfeea744..09f50ad2a 100644
--- a/src/resources/gtk/prefs-dialog.ui
+++ b/src/resources/gtk/prefs-dialog.ui
@@ -28,338 +28,7 @@
<property name="scrollable">True</property>
<property name="show_border">False</property>
<child>
- <object class="HdyPreferencesPage">
- <property name="icon_name">applications-system-symbolic</property>
- <property name="title" translatable="yes">General</property>
- <property name="visible">True</property>
- <style>
- <class name="background"/>
- </style>
- <child>
- <object class="HdyPreferencesGroup" id="webapp_box">
- <property name="title" translatable="yes">Web Application</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyPreferencesRow">
- <property name="activatable">False</property>
- <property name="visible">True</property>
- <child>
- <object class="GtkGrid">
- <property name="column-spacing">12</property>
- <property name="margin-bottom">8</property>
- <property name="margin-end">12</property>
- <property name="margin-start">12</property>
- <property name="margin-top">8</property>
- <property name="row-spacing">6</property>
- <property name="visible">True</property>
- <child>
- <object class="GtkButton" id="webapp_icon_button">
- <property name="visible">true</property>
- <property name="halign">center</property>
- <signal name="clicked" handler="on_webapp_icon_button_clicked"/>
- <child>
- <object class="GtkImage" id="webapp_icon">
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="left-attach">0</property>
- <property name="top-attach">0</property>
- <property name="height">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Homepage:</property>
- </object>
- <packing>
- <property name="left-attach">1</property>
- <property name="top-attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="webapp_url">
- <property name="visible">True</property>
- <property name="hexpand">True</property>
- <signal name="changed" handler="on_webapp_entry_changed"/>
- </object>
- <packing>
- <property name="left-attach">2</property>
- <property name="top-attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Title:</property>
- </object>
- <packing>
- <property name="left-attach">1</property>
- <property name="top-attach">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="webapp_title">
- <property name="visible">True</property>
- <property name="hexpand">True</property>
- <signal name="changed" handler="on_webapp_entry_changed"/>
- </object>
- <packing>
- <property name="left-attach">2</property>
- <property name="top-attach">1</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable">False</property>
- <property name="title" translatable="yes">_Manage Additional URLs</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkButton" id="webapp_additional_urls_dialog_button">
- <property name="valign">center</property>
- <property name="visible">True</property>
- <signal name="clicked"
handler="on_manage_webapp_additional_urls_button_clicked"/>
- <style>
- <class name="image-button"/>
- </style>
- <child>
- <object class="GtkImage">
- <property name="icon_name">emblem-system-symbolic</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyPreferencesGroup" id="homepage_box">
- <property name="title" translatable="yes">Homepage</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">new_tab_homepage_radiobutton</property>
- <property name="title" translatable="yes">Most _visited pages</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="prefix">
- <object class="GtkRadioButton" id="new_tab_homepage_radiobutton">
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">blank_homepage_radiobutton</property>
- <property name="title" translatable="yes">_Blank page</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="prefix">
- <object class="GtkRadioButton" id="blank_homepage_radiobutton">
- <property name="valign">center</property>
- <property name="group">new_tab_homepage_radiobutton</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">custom_homepage_radiobutton</property>
- <property name="title" translatable="yes">_Custom</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="prefix">
- <object class="GtkRadioButton" id="custom_homepage_radiobutton">
- <property name="valign">center</property>
- <property name="group">new_tab_homepage_radiobutton</property>
- <property name="visible">True</property>
- </object>
- </child>
- <child type="action">
- <object class="GtkEntry" id="custom_homepage_entry">
- <property name="hexpand">True</property>
- <property name="secondary-icon-name">edit-clear-symbolic</property>
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyPreferencesGroup" id="download_box">
- <property name="title" translatable="yes">Downloads</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">ask_on_download_switch</property>
- <property name="title" translatable="yes">Ask o_n download</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkSwitch" id="ask_on_download_switch">
- <property name="margin-start">12</property>
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow" id="download_folder_row">
- <property name="sensitive" bind-source="ask_on_download_switch"
bind-property="active" bind-flags="sync-create|invert-boolean"/>
- <property name="title" translatable="yes">_Download folder</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyPreferencesGroup" id="search_box">
- <property name="title" translatable="yes">Search Engines</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyActionRow">
- <property name="activatable">False</property>
- <property name="title" translatable="yes">_Manage Search Engines</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkButton" id="search_engine_dialog_button">
- <property name="valign">center</property>
- <property name="visible">True</property>
- <signal name="clicked" handler="on_search_engine_dialog_button_clicked"/>
- <style>
- <class name="image-button"/>
- </style>
- <child>
- <object class="GtkImage">
- <property name="icon_name">emblem-system-symbolic</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyPreferencesGroup" id="session_box">
- <property name="title" translatable="yes">Session</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">start_in_incognito_mode_switch</property>
- <property name="title" translatable="yes">Start in _Incognito Mode</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkSwitch" id="start_in_incognito_mode_switch">
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow" id="restore_session_row">
- <property name="activatable_widget">restore_session_switch</property>
- <property name="title" translatable="yes">_Restore Tabs on Startup</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkSwitch" id="restore_session_switch">
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyPreferencesGroup" id="browsing_box">
- <property name="title" translatable="yes">Browsing</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">enable_smooth_scrolling_switch</property>
- <property name="title" translatable="yes">Sm_ooth Scrolling</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkSwitch" id="enable_smooth_scrolling_switch">
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">enable_mouse_gesture_switch</property>
- <property name="title" translatable="yes">Mouse _Gestures</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkSwitch" id="enable_mouse_gesture_switch">
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyPreferencesGroup" id="lang_group">
- <property name="title" translatable="yes">Languages</property>
- <property name="visible">True</property>
- <child>
- <object class="GtkListBox" id="lang_listbox">
- <property name="selection_mode">none</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyPreferencesGroup">
- <property name="title" translatable="yes"></property>
- <property name="visible">True</property>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">enable_spell_checking_switch</property>
- <property name="title" translatable="yes">_Spell Checking</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkSwitch" id="enable_spell_checking_switch">
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
+ <object class="PrefsGeneralPage" id="general_page">
</object>
</child>
<child type="tab">
@@ -372,210 +41,7 @@
</packing>
</child>
<child>
- <object class="HdyPreferencesPage">
- <property name="icon_name">preferences-system-privacy-symbolic</property>
- <property name="title" translatable="yes">Privacy</property>
- <property name="visible">True</property>
- <style>
- <class name="background"/>
- </style>
- <child>
- <object class="HdyPreferencesGroup">
- <property name="title" translatable="yes">Web Content</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyActionRow" id="adblock_allow_row">
- <property name="activatable_widget">adblock_allow_switch</property>
- <property name="title" translatable="yes">Try to Block _Advertisements</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkSwitch" id="adblock_allow_switch">
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">popups_allow_switch</property>
- <property name="title" translatable="yes">Block Popup _Windows</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkSwitch" id="popups_allow_switch">
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">enable_safe_browsing_switch</property>
- <property name="title" translatable="yes">Try to Block Dangerous Web_sites</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkSwitch" id="enable_safe_browsing_switch">
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyPreferencesGroup">
- <property name="title" translatable="yes">Cookies</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyActionRow">
- <property name="activatable">False</property>
- <property name="title" translatable="yes">Clear _Cookies</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkButton">
- <property name="valign">center</property>
- <property name="visible">True</property>
- <signal name="clicked" handler="on_manage_cookies_button_clicked"/>
- <style>
- <class name="image-button"/>
- </style>
- <child>
- <object class="GtkImage">
- <property name="icon_name">emblem-system-symbolic</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">always</property>
- <property name="title" translatable="yes">_Always accept</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="prefix">
- <object class="GtkRadioButton" id="always">
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">no_third_party</property>
- <property name="subtitle" translatable="yes" comments="Refers to "Only from
sites you visit" option under Cookies.">For example, not from advertisers on these sites.</property>
- <property name="title" translatable="yes">Only _from sites you visit</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="prefix">
- <object class="GtkRadioButton" id="no_third_party">
- <property name="valign">center</property>
- <property name="group">always</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">never</property>
- <property name="title" translatable="yes">_Never accept</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="prefix">
- <object class="GtkRadioButton" id="never">
- <property name="valign">center</property>
- <property name="group">always</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyPreferencesGroup">
- <property name="title" translatable="yes">Passwords</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyActionRow">
- <property name="activatable">False</property>
- <property name="title" translatable="yes">_Passwords</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkButton">
- <property name="valign">center</property>
- <property name="visible">True</property>
- <signal name="clicked" handler="on_manage_passwords_button_clicked"/>
- <style>
- <class name="image-button"/>
- </style>
- <child>
- <object class="GtkImage">
- <property name="icon_name">emblem-system-symbolic</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">remember_passwords_switch</property>
- <property name="title" translatable="yes">_Remember Passwords</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkSwitch" id="remember_passwords_switch">
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyPreferencesGroup">
- <property name="title" translatable="yes">Personal Data</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyActionRow">
- <property name="activatable">False</property>
- <property name="subtitle" translatable="yes">You can clear stored personal
data.</property>
- <property name="title" translatable="yes">Clear Personal _Data</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkButton" id="clear_personal_data_button">
- <property name="valign">center</property>
- <property name="visible">True</property>
- <style>
- <class name="image-button"/>
- </style>
- <child>
- <object class="GtkImage">
- <property name="icon_name">emblem-system-symbolic</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
+ <object class="PrefsPrivacyPage">
</object>
<packing>
<property name="position">1</property>
@@ -592,203 +58,7 @@
</packing>
</child>
<child>
- <object class="HdyPreferencesPage">
- <property name="icon_name">document-edit-symbolic</property>
- <property name="title" translatable="yes">Appearance</property>
- <property name="visible">True</property>
- <style>
- <class name="background"/>
- </style>
- <child>
- <object class="HdyPreferencesGroup">
- <property name="title" translatable="yes">Fonts</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyExpanderRow" id="use_gnome_fonts_row">
- <property name="show_enable_switch">True</property>
- <property name="title" translatable="yes">Use Custom Fonts</property>
- <property name="visible">True</property>
- <child>
- <object class="GtkSeparator">
- <property name="margin-start">24</property>
- <property name="visible">True</property>
- </object>
- </child>
- <child>
- <object class="GtkListBox" id="use_custom_fonts_list">
- <property name="margin-start">24</property>
- <property name="selection_mode">none</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyActionRow">
- <property name="activatable">False</property>
- <property name="title" translatable="yes">Sans serif font</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkFontButton" id="sans_fontbutton">
- <property name="font">Sans 12</property>
- <property name="use-font">True</property>
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable">False</property>
- <property name="title" translatable="yes">Serif font</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkFontButton" id="serif_fontbutton">
- <property name="font">Sans 12</property>
- <property name="use-font">True</property>
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable">False</property>
- <property name="title" translatable="yes">Monospace font</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkFontButton" id="mono_fontbutton">
- <property name="font">Sans 12</property>
- <property name="use-font">True</property>
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyPreferencesGroup" id="reader_mode_box">
- <property name="title" translatable="yes">Reader Mode</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyComboRow" id="reader_mode_font_style">
- <property name="title" translatable="yes">Font Style</property>
- <property name="visible">True</property>
- </object>
- </child>
- <child>
- <object class="HdyComboRow" id="reader_mode_color_scheme">
- <property name="title" translatable="yes">Color Scheme</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
- <object class="HdyPreferencesGroup">
- <property name="title" translatable="yes">Style</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">css_switch</property>
- <property name="title" translatable="yes">Use Custom Stylesheet</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkButton" id="css_edit_button">
- <property name="valign">center</property>
- <property name="visible">True</property>
- <style>
- <class name="image-button"/>
- </style>
- <child>
- <object class="GtkImage">
- <property name="icon_name">document-edit-symbolic</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child type="action">
- <object class="GtkSeparator">
- <property name="margin_bottom">8</property>
- <property name="margin_top">8</property>
- <property name="visible">True</property>
- </object>
- </child>
- <child type="action">
- <object class="GtkSwitch" id="css_switch">
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">js_switch</property>
- <property name="title" translatable="yes">Use Custom JavaScript</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkButton" id="js_edit_button">
- <property name="valign">center</property>
- <property name="visible">True</property>
- <style>
- <class name="image-button"/>
- </style>
- <child>
- <object class="GtkImage">
- <property name="icon_name">document-edit-symbolic</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child type="action">
- <object class="GtkSeparator">
- <property name="margin_bottom">8</property>
- <property name="margin_top">8</property>
- <property name="visible">True</property>
- </object>
- </child>
- <child type="action">
- <object class="GtkSwitch" id="js_switch">
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable">False</property>
- <property name="title" translatable="yes">Default zoom level</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkSpinButton" id="default_zoom_spin_button">
- <property name="adjustment">zoom_adjustment</property>
- <property name="can_focus">True</property>
- <property name="input_purpose">number</property>
- <property name="max_width_chars">5</property>
- <property name="valign">center</property>
- <property name="value">100</property>
- <property name="visible">True</property>
- <signal name="output" handler="on_default_zoom_spin_button_output"/>
- <signal name="value-changed"
handler="on_default_zoom_spin_button_value_changed"/>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
+ <object class="PrefsAppearancePage">
</object>
<packing>
<property name="position">2</property>
@@ -805,197 +75,7 @@
</packing>
</child>
<child>
- <object class="HdyPreferencesPage" id="sync_page">
- <property name="icon_name">emblem-synchronizing-symbolic</property>
- <property name="title" translatable="yes">Sync</property>
- <property name="visible">True</property>
- <style>
- <class name="background"/>
- </style>
- <child>
- <object class="HdyPreferencesGroup" id="sync_page_box">
- <property name="description" translatable="yes">Sign in with your Firefox account to
sync your data with Web and Firefox on other computers. Web is not Firefox and is not produced or endorsed by
Mozilla.</property>
- <property name="title" translatable="yes">Firefox Sync</property>
- <property name="visible">True</property>
- <child>
- <object class="GtkBox" id="sync_firefox_iframe_box">
- <property name="visible">True</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkLabel" id="sync_firefox_iframe_label">
- <property name="visible">False</property>
- <property name="halign">start</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyPreferencesGroup" id="sync_firefox_account_box">
- <property name="title" translatable="yes">Firefox Account</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyActionRow" id="sync_firefox_account_row">
- <property name="subtitle" translatable="yes">Logged in</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkButton" id="sync_sign_out_button">
- <property name="label" translatable="yes">Sign _out</property>
- <property name="use-underline">True</property>
- <property name="valign">center</property>
- <property name="visible">True</property>
- <signal name="clicked" handler="on_sync_sign_out_button_clicked"/>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyPreferencesGroup" id="sync_options_box">
- <property name="title" translatable="yes">Sync Options</property>
- <property name="visible">True</property>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">sync_bookmarks_checkbutton</property>
- <property name="title" translatable="yes">Sync _Bookmarks</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="prefix">
- <object class="GtkCheckButton" id="sync_bookmarks_checkbutton">
- <property name="use-underline">True</property>
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">sync_passwords_checkbutton</property>
- <property name="title" translatable="yes">Sync _Passwords</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="prefix">
- <object class="GtkCheckButton" id="sync_passwords_checkbutton">
- <property name="use-underline">True</property>
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">sync_history_checkbutton</property>
- <property name="title" translatable="yes">Sync _History</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="prefix">
- <object class="GtkCheckButton" id="sync_history_checkbutton">
- <property name="use-underline">True</property>
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable_widget">sync_open_tabs_checkbutton</property>
- <property name="title" translatable="yes">Sync Open _Tabs</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="prefix">
- <object class="GtkCheckButton" id="sync_open_tabs_checkbutton">
- <property name="use-underline">True</property>
- <property name="valign">center</property>
- <property name="visible">True</property>
- </object>
- </child>
- <child type="action">
- <object class="GtkButton" id="synced_tabs_button">
- <property name="label" translatable="yes">S_ynced tabs</property>
- <property name="use-underline">True</property>
- <property name="valign">center</property>
- <property name="visible">True</property>
- <signal name="clicked" handler="on_sync_synced_tabs_button_clicked"/>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyComboRow" id="sync_frequency_row">
- <property name="title" translatable="yes">Frequency</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkSeparator">
- <property name="margin_bottom">8</property>
- <property name="margin_top">8</property>
- <property name="visible">True</property>
- </object>
- </child>
- <child type="action">
- <object class="GtkButton" id="sync_now_button">
- <property name="label" translatable="yes">Sync _now</property>
- <property name="use-underline">True</property>
- <property name="valign">center</property>
- <property name="visible">True</property>
- <signal name="clicked" handler="on_sync_sync_now_button_clicked"/>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="HdyActionRow">
- <property name="activatable">False</property>
- <property name="title" translatable="yes">Device name</property>
- <property name="use_underline">True</property>
- <property name="visible">True</property>
- <child type="action">
- <object class="GtkButton" id="sync_device_name_change_button">
- <property name="label" translatable="yes">_Change</property>
- <property name="use-underline">True</property>
- <property name="valign">center</property>
- <property name="visible">True</property>
- <signal name="clicked" handler="on_sync_device_name_change_button_clicked"/>
- </object>
- </child>
- <child type="action">
- <object class="GtkButton" id="sync_device_name_save_button">
- <property name="label" translatable="yes">_Save</property>
- <property name="use-underline">True</property>
- <property name="valign">center</property>
- <property name="visible">False</property>
- <signal name="clicked" handler="on_sync_device_name_save_button_clicked"/>
- </object>
- </child>
- <child type="action">
- <object class="GtkButton" id="sync_device_name_cancel_button">
- <property name="label" translatable="yes">_Cancel</property>
- <property name="use-underline">True</property>
- <property name="valign">center</property>
- <property name="visible">False</property>
- <signal name="clicked" handler="on_sync_device_name_cancel_button_clicked"/>
- </object>
- </child>
- <child>
- <object class="GtkEntry" id="sync_device_name_entry">
- <property name="sensitive">False</property>
- <property name="margin-start">12</property>
- <property name="margin-end">12</property>
- <property name="margin-top">8</property>
- <property name="margin-bottom">8</property>
- <property name="visible">True</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
+ <object class="PrefsSyncPage" id="sync_page">
</object>
</child>
<child type="tab">
@@ -1015,27 +95,4 @@
</object>
</child>
</template>
- <object class="GtkSizeGroup">
- <property name="mode">horizontal</property>
- <widgets>
- <widget name="sans_fontbutton"/>
- <widget name="serif_fontbutton"/>
- <widget name="mono_fontbutton"/>
- </widgets>
- </object>
- <object class="GtkSizeGroup">
- <property name="mode">horizontal</property>
- <widgets>
- <widget name="sync_sign_out_button"/>
- <widget name="synced_tabs_button"/>
- <widget name="sync_now_button"/>
- <widget name="sync_device_name_change_button"/>
- </widgets>
- </object>
- <object class="GtkAdjustment" id="zoom_adjustment">
- <property name="lower">33</property>
- <property name="upper">300</property>
- <property name="step_increment">1</property>
- <property name="page_increment">10</property>
- </object>
</interface>
diff --git a/src/resources/gtk/prefs-general-page.ui b/src/resources/gtk/prefs-general-page.ui
new file mode 100644
index 000000000..e13489aab
--- /dev/null
+++ b/src/resources/gtk/prefs-general-page.ui
@@ -0,0 +1,337 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.0"/>
+ <template class="PrefsGeneralPage" parent="HdyPreferencesPage">
+ <property name="icon_name">applications-system-symbolic</property>
+ <property name="title" translatable="yes">General</property>
+ <property name="visible">True</property>
+ <style>
+ <class name="background"/>
+ </style>
+ <child>
+ <object class="HdyPreferencesGroup" id="webapp_box">
+ <property name="title" translatable="yes">Web Application</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyPreferencesRow">
+ <property name="activatable">False</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkGrid">
+ <property name="column-spacing">12</property>
+ <property name="margin-bottom">8</property>
+ <property name="margin-end">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">8</property>
+ <property name="row-spacing">6</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkButton" id="webapp_icon_button">
+ <property name="visible">true</property>
+ <property name="halign">center</property>
+ <signal name="clicked" handler="on_webapp_icon_button_clicked"/>
+ <child>
+ <object class="GtkImage" id="webapp_icon">
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Homepage:</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="webapp_url">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <signal name="changed" handler="on_webapp_entry_changed"/>
+ </object>
+ <packing>
+ <property name="left-attach">2</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Title:</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="webapp_title">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <signal name="changed" handler="on_webapp_entry_changed"/>
+ </object>
+ <packing>
+ <property name="left-attach">2</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">_Manage Additional URLs</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkButton" id="webapp_additional_urls_dialog_button">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ <signal name="clicked" handler="on_manage_webapp_additional_urls_button_clicked"/>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon_name">emblem-system-symbolic</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyPreferencesGroup" id="homepage_box">
+ <property name="title" translatable="yes">Homepage</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">new_tab_homepage_radiobutton</property>
+ <property name="title" translatable="yes">Most _visited pages</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="prefix">
+ <object class="GtkRadioButton" id="new_tab_homepage_radiobutton">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">blank_homepage_radiobutton</property>
+ <property name="title" translatable="yes">_Blank page</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="prefix">
+ <object class="GtkRadioButton" id="blank_homepage_radiobutton">
+ <property name="valign">center</property>
+ <property name="group">new_tab_homepage_radiobutton</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">custom_homepage_radiobutton</property>
+ <property name="title" translatable="yes">_Custom</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="prefix">
+ <object class="GtkRadioButton" id="custom_homepage_radiobutton">
+ <property name="valign">center</property>
+ <property name="group">new_tab_homepage_radiobutton</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkEntry" id="custom_homepage_entry">
+ <property name="hexpand">True</property>
+ <property name="secondary-icon-name">edit-clear-symbolic</property>
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyPreferencesGroup" id="download_box">
+ <property name="title" translatable="yes">Downloads</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">ask_on_download_switch</property>
+ <property name="title" translatable="yes">Ask o_n download</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkSwitch" id="ask_on_download_switch">
+ <property name="margin-start">12</property>
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow" id="download_folder_row">
+ <property name="sensitive" bind-source="ask_on_download_switch" bind-property="active"
bind-flags="sync-create|invert-boolean"/>
+ <property name="title" translatable="yes">_Download folder</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyPreferencesGroup" id="search_box">
+ <property name="title" translatable="yes">Search Engines</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">_Manage Search Engines</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkButton" id="search_engine_dialog_button">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ <signal name="clicked" handler="on_search_engine_dialog_button_clicked"/>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon_name">emblem-system-symbolic</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyPreferencesGroup" id="session_box">
+ <property name="title" translatable="yes">Session</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">start_in_incognito_mode_switch</property>
+ <property name="title" translatable="yes">Start in _Incognito Mode</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkSwitch" id="start_in_incognito_mode_switch">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow" id="restore_session_row">
+ <property name="activatable_widget">restore_session_switch</property>
+ <property name="title" translatable="yes">_Restore Tabs on Startup</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkSwitch" id="restore_session_switch">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyPreferencesGroup" id="browsing_box">
+ <property name="title" translatable="yes">Browsing</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">enable_smooth_scrolling_switch</property>
+ <property name="title" translatable="yes">Sm_ooth Scrolling</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkSwitch" id="enable_smooth_scrolling_switch">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">enable_mouse_gesture_switch</property>
+ <property name="title" translatable="yes">Mouse _Gestures</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkSwitch" id="enable_mouse_gesture_switch">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyPreferencesGroup" id="lang_group">
+ <property name="title" translatable="yes">Languages</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkListBox" id="lang_listbox">
+ <property name="selection_mode">none</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyPreferencesGroup">
+ <property name="title" translatable="yes"></property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">enable_spell_checking_switch</property>
+ <property name="title" translatable="yes">_Spell Checking</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkSwitch" id="enable_spell_checking_switch">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/resources/gtk/prefs-privacy-page.ui b/src/resources/gtk/prefs-privacy-page.ui
new file mode 100644
index 000000000..c3b5d5107
--- /dev/null
+++ b/src/resources/gtk/prefs-privacy-page.ui
@@ -0,0 +1,209 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.0"/>
+ <template class="PrefsPrivacyPage" parent="HdyPreferencesPage">
+ <property name="icon_name">preferences-system-privacy-symbolic</property>
+ <property name="title" translatable="yes">Privacy</property>
+ <property name="visible">True</property>
+ <style>
+ <class name="background"/>
+ </style>
+ <child>
+ <object class="HdyPreferencesGroup">
+ <property name="title" translatable="yes">Web Content</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyActionRow" id="adblock_allow_row">
+ <property name="activatable_widget">adblock_allow_switch</property>
+ <property name="title" translatable="yes">Try to Block _Advertisements</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkSwitch" id="adblock_allow_switch">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">popups_allow_switch</property>
+ <property name="title" translatable="yes">Block Popup _Windows</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkSwitch" id="popups_allow_switch">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">enable_safe_browsing_switch</property>
+ <property name="title" translatable="yes">Try to Block Dangerous Web_sites</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkSwitch" id="enable_safe_browsing_switch">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyPreferencesGroup">
+ <property name="title" translatable="yes">Cookies</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">Clear _Cookies</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkButton">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ <signal name="clicked" handler="on_manage_cookies_button_clicked"/>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon_name">emblem-system-symbolic</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">always</property>
+ <property name="title" translatable="yes">_Always accept</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="prefix">
+ <object class="GtkRadioButton" id="always">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">no_third_party</property>
+ <property name="subtitle" translatable="yes" comments="Refers to "Only from sites you
visit" option under Cookies.">For example, not from advertisers on these sites.</property>
+ <property name="title" translatable="yes">Only _from sites you visit</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="prefix">
+ <object class="GtkRadioButton" id="no_third_party">
+ <property name="valign">center</property>
+ <property name="group">always</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">never</property>
+ <property name="title" translatable="yes">_Never accept</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="prefix">
+ <object class="GtkRadioButton" id="never">
+ <property name="valign">center</property>
+ <property name="group">always</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyPreferencesGroup">
+ <property name="title" translatable="yes">Passwords</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">_Passwords</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkButton">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ <signal name="clicked" handler="on_manage_passwords_button_clicked"/>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon_name">emblem-system-symbolic</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">remember_passwords_switch</property>
+ <property name="title" translatable="yes">_Remember Passwords</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkSwitch" id="remember_passwords_switch">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyPreferencesGroup">
+ <property name="title" translatable="yes">Personal Data</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable">False</property>
+ <property name="subtitle" translatable="yes">You can clear stored personal data.</property>
+ <property name="title" translatable="yes">Clear Personal _Data</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkButton" id="clear_personal_data_button">
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon_name">emblem-system-symbolic</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/resources/gtk/prefs-sync-page.ui b/src/resources/gtk/prefs-sync-page.ui
new file mode 100644
index 000000000..f79f5bcc4
--- /dev/null
+++ b/src/resources/gtk/prefs-sync-page.ui
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.0"/>
+ <template class="PrefsSyncPage" parent="HdyPreferencesPage">
+ <property name="icon_name">emblem-synchronizing-symbolic</property>
+ <property name="title" translatable="yes">Sync</property>
+ <property name="visible">True</property>
+ <style>
+ <class name="background"/>
+ </style>
+ <child>
+ <object class="HdyPreferencesGroup" id="sync_page_box">
+ <property name="description" translatable="yes">Sign in with your Firefox account to sync your data
with Web and Firefox on other computers. Web is not Firefox and is not produced or endorsed by
Mozilla.</property>
+ <property name="title" translatable="yes">Firefox Sync</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkBox" id="sync_firefox_iframe_box">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="sync_firefox_iframe_label">
+ <property name="visible">False</property>
+ <property name="halign">start</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyPreferencesGroup" id="sync_firefox_account_box">
+ <property name="title" translatable="yes">Firefox Account</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyActionRow" id="sync_firefox_account_row">
+ <property name="subtitle" translatable="yes">Logged in</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkButton" id="sync_sign_out_button">
+ <property name="label" translatable="yes">Sign _out</property>
+ <property name="use-underline">True</property>
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ <signal name="clicked" handler="on_sync_sign_out_button_clicked"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyPreferencesGroup" id="sync_options_box">
+ <property name="title" translatable="yes">Sync Options</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">sync_bookmarks_checkbutton</property>
+ <property name="title" translatable="yes">Sync _Bookmarks</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="prefix">
+ <object class="GtkCheckButton" id="sync_bookmarks_checkbutton">
+ <property name="use-underline">True</property>
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">sync_passwords_checkbutton</property>
+ <property name="title" translatable="yes">Sync _Passwords</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="prefix">
+ <object class="GtkCheckButton" id="sync_passwords_checkbutton">
+ <property name="use-underline">True</property>
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">sync_history_checkbutton</property>
+ <property name="title" translatable="yes">Sync _History</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="prefix">
+ <object class="GtkCheckButton" id="sync_history_checkbutton">
+ <property name="use-underline">True</property>
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable_widget">sync_open_tabs_checkbutton</property>
+ <property name="title" translatable="yes">Sync Open _Tabs</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="prefix">
+ <object class="GtkCheckButton" id="sync_open_tabs_checkbutton">
+ <property name="use-underline">True</property>
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkButton" id="synced_tabs_button">
+ <property name="label" translatable="yes">S_ynced tabs</property>
+ <property name="use-underline">True</property>
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ <signal name="clicked" handler="on_sync_synced_tabs_button_clicked"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyComboRow" id="sync_frequency_row">
+ <property name="title" translatable="yes">Frequency</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkSeparator">
+ <property name="margin_bottom">8</property>
+ <property name="margin_top">8</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkButton" id="sync_now_button">
+ <property name="label" translatable="yes">Sync _now</property>
+ <property name="use-underline">True</property>
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ <signal name="clicked" handler="on_sync_sync_now_button_clicked"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow">
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">Device name</property>
+ <property name="use_underline">True</property>
+ <property name="visible">True</property>
+ <child type="action">
+ <object class="GtkButton" id="sync_device_name_change_button">
+ <property name="label" translatable="yes">_Change</property>
+ <property name="use-underline">True</property>
+ <property name="valign">center</property>
+ <property name="visible">True</property>
+ <signal name="clicked" handler="on_sync_device_name_change_button_clicked"/>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkButton" id="sync_device_name_save_button">
+ <property name="label" translatable="yes">_Save</property>
+ <property name="use-underline">True</property>
+ <property name="valign">center</property>
+ <property name="visible">False</property>
+ <signal name="clicked" handler="on_sync_device_name_save_button_clicked"/>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkButton" id="sync_device_name_cancel_button">
+ <property name="label" translatable="yes">_Cancel</property>
+ <property name="use-underline">True</property>
+ <property name="valign">center</property>
+ <property name="visible">False</property>
+ <signal name="clicked" handler="on_sync_device_name_cancel_button_clicked"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="sync_device_name_entry">
+ <property name="sensitive">False</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-top">8</property>
+ <property name="margin-bottom">8</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkSizeGroup">
+ <property name="mode">horizontal</property>
+ <widgets>
+ <widget name="sync_sign_out_button"/>
+ <widget name="synced_tabs_button"/>
+ <widget name="sync_now_button"/>
+ <widget name="sync_device_name_change_button"/>
+ </widgets>
+ </object>
+</interface>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]